<!DOCTYPE html><html lang="en" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>【MQ】RabbitMQ | ChenWeiHui's Blog</title><meta name="author" content="cwh"><meta name="copyright" content="cwh"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="RabbitMQ概念​        RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点，当你要发送一个包裹时，你把你的包裹放到快递站，快递员最终会把你的快递送到收件人那里，按照这种逻辑 RabbitMQ是一个快递站，一个快递员帮你传递快件。RabbitMQ与快递站的主要区别在于，它不处理快件而是接收存储和转发消息数据。  四大核心概念生产者产生数据发送消息的程序是生">
<meta property="og:type" content="article">
<meta property="og:title" content="【MQ】RabbitMQ">
<meta property="og:url" content="https://gitee.com/CWH6/2023/05/05/%E3%80%90RabbitMQ%E3%80%91%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/index.html">
<meta property="og:site_name" content="ChenWeiHui&#39;s Blog">
<meta property="og:description" content="RabbitMQ概念​        RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点，当你要发送一个包裹时，你把你的包裹放到快递站，快递员最终会把你的快递送到收件人那里，按照这种逻辑 RabbitMQ是一个快递站，一个快递员帮你传递快件。RabbitMQ与快递站的主要区别在于，它不处理快件而是接收存储和转发消息数据。  四大核心概念生产者产生数据发送消息的程序是生">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/xhs.png">
<meta property="article:published_time" content="2023-05-05T14:52:00.000Z">
<meta property="article:modified_time" content="2023-05-14T03:21:26.220Z">
<meta property="article:author" content="cwh">
<meta property="article:tag" content="MQ">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/xhs.png"><link rel="shortcut icon" href="/bk/img/jljy3.png"><link rel="canonical" href="https://gitee.com/CWH6/2023/05/05/%E3%80%90RabbitMQ%E3%80%91%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/bk/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/bk/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: 'Copy successfully',
    error: 'Copy error',
    noSupport: 'The browser does not support'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  dateSuffix: {
    just: 'Just',
    min: 'minutes ago',
    hour: 'hours ago',
    day: 'days ago',
    month: 'months ago'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '【MQ】RabbitMQ',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-05-14 11:21:26'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 5.4.2"></head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/xhs.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/bk/archives/"><div class="headline">Articles</div><div class="length-num">66</div></a><a href="/bk/tags/"><div class="headline">Tags</div><div class="length-num">33</div></a><a href="/bk/categories/"><div class="headline">Categories</div><div class="length-num">8</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/bk/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/bk/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/bk/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/bk/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> List</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/bk/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bk/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/bk/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/bk/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/bk/img/alone_blue_sky.jpg')"><nav id="nav"><span id="blog-info"><a href="/bk/" title="ChenWeiHui's Blog"><span class="site-name">ChenWeiHui's Blog</span></a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/bk/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/bk/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/bk/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/bk/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> List</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/bk/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bk/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/bk/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/bk/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">【MQ】RabbitMQ</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">Created</span><time class="post-meta-date-created" datetime="2023-05-05T14:52:00.000Z" title="Created 2023-05-05 22:52:00">2023-05-05</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">Updated</span><time class="post-meta-date-updated" datetime="2023-05-14T03:21:26.220Z" title="Updated 2023-05-14 11:21:26">2023-05-14</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/bk/categories/%E5%90%8E%E7%AB%AF/">后端</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="【MQ】RabbitMQ"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">Post View:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h3 id="RabbitMQ概念"><a href="#RabbitMQ概念" class="headerlink" title="RabbitMQ概念"></a>RabbitMQ概念</h3><p>​        RabbitMQ 是一个<strong>消息中间件</strong>:它接受并转发消息。你可以把它当做一个<strong>快递站点</strong>，当你要发送一个包裹时，你把你的包裹放到快递站，快递员最终会把你的快递送到收件人那里，按照这种逻辑 RabbitMQ是一个快递站，一个快递员帮你传递快件。RabbitMQ与快递站的主要区别在于，它不处理快件而是<strong>接收存储和转发消息数据</strong>。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230506105149780.png"></p>
<h3 id="四大核心概念"><a href="#四大核心概念" class="headerlink" title="四大核心概念"></a>四大核心概念</h3><h4 id="生产者"><a href="#生产者" class="headerlink" title="生产者"></a>生产者</h4><p><strong>产生数据发送消息</strong>的程序是生产者</p>
<h4 id="交换机"><a href="#交换机" class="headerlink" title="交换机"></a>交换机</h4><p>交换机是 RabbitMQ 非常重要的一个部件，一方面它<strong>接收来自生产者的消息</strong>，另一方面它<strong>将消息推送到队列中</strong>。交换机必须确切知道如何处理它接收到的消息，是将这些消息推送到特定队列还是推送到多个队列，亦或者是把消息丢弃，这个得有交换机类型决定</p>
<h4 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h4><p>队列是 RabbitMQ 内部使用的一种数据结构，尽管消息流经 RabbitMQ和应用程序，但它们只能<strong>存储在队列中</strong>。队列仅受主机的内存和磁盘限制的约束，<strong>本质上是一个大的消息缓冲区</strong>。许多生产者可以将消息发送到一个队列，许多消费者可以尝试从一个队列接收数据。这就是我们使用队列的方式</p>
<h4 id="消费者"><a href="#消费者" class="headerlink" title="消费者"></a>消费者</h4><p>消费与接收具有相似的含义。消费者大多时候是一个<strong>等待接收消息的程序</strong>。请注意<strong>生产者，消费者和消息中间件很多时候并不在同一机器上</strong>。<strong>同一个应用程序既可以是生产者又是可以是消费者。</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230506105736761.png"></p>
<h3 id="RabbitMQ模式与原理"><a href="#RabbitMQ模式与原理" class="headerlink" title="RabbitMQ模式与原理"></a>RabbitMQ模式与原理</h3><h4 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h4><p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230506111310491.png"></p>
<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230506110041118.png"></p>
<p><strong>Broker</strong>（mq实体）</p>
<p>​         接收和分发消息的应用，<em>RabbitMQ Server</em> 就是 <em>Message Broker</em></p>
<p><strong>Virtual host</strong>:</p>
<p>​         出于多租户和安全因素设计的，把 <em>AMQP</em> 的基本组件划分到一个虚拟的分组中，类似于网络中的<em>namespace</em> 概念。当多个不同的用户使用同一个RabbitMQ server 提供的服务时，可以划分出多个vhost，每个用户在自己的 <em>vhost</em> 创建 <em>exchange /queue</em> 等</p>
<p><strong>Connection:</strong> </p>
<p>​        <em>publisher /consumer</em> 和 <em>broker</em> 之间的TCP连接</p>
<p><strong>chahnel:</strong>  (信道) </p>
<p>​     <strong>如果每一次访问 RabbitMQ 都建立一个 Connection，在消息量大的时候建立 TCPConnection 的开销将是巨大的，效率也较低</strong>。Channel 是在connection 内部建立的逻辑连接，如果应用程序支持多线程，通常每个 thread 创建单独的 channel 进行通讯，AMQP method 包含了 channel id 帮助客户端和message broker 识别 channel，所以 channel 之间是完全隔离的。Channel作为轻量级的连接Connection 极大减少了操作系统建立TCP connection 的开销  , <strong>生产者每次发送只会占用一个信道</strong>。</p>
<p><strong>Exchange</strong> ( 交换机 ) :</p>
<p>​         message 到达 broker 的第一站，<strong>根据分发规则，匹配查询表中的 routing key，分发消息到 queue 中去</strong>。常用的类型有: direct (point-to-point), topit (publish-subscribe) and fanout(multicast)</p>
<p><strong>Queue</strong>（队列）:</p>
<p>​         消息最终被送到这里等待 consumer 取走</p>
<p><strong>Binding</strong>（绑定 ）:</p>
<p>​    <strong>exchange 和queue 之间的虚拟连接，binding 中可以包含 routing key</strong>，Binding 信息被保存到exchange 中的查询表中，用于 message 的分发依据。</p>
<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>这里直接下在 <em>Linux</em> , 可以采用Docker方式安装 ( 推荐 )</p>
<h4 id="Linux下安装MQ"><a href="#Linux下安装MQ" class="headerlink" title="Linux下安装MQ"></a>Linux下安装MQ</h4><p><strong>官网地址</strong></p>
<p><a target="_blank" rel="noopener" href="https://www.rabbitmq.com/download.html">https://www.rabbitmq.com/download.html</a></p>
<p><strong>文件上传</strong></p>
<p>上传到/usr/local/software 目录下(如果没有 software 需要自己创建, 然后下面的步骤都在改目录下执行</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir software</span><br><span class="line">cd software</span><br></pre></td></tr></table></figure>

<p><strong>安装文件(分别按照以下顺序安装)</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">i 下载 , vh 查看下载进度</span> </span><br><span class="line">rpm -ivh erlang-21.3-1.el7.x86_64.rpm</span><br><span class="line">yum install socat -y</span><br><span class="line">rpm -ivh rabbitmq-server-3.8.8-1.el7.noarch.rpm</span><br></pre></td></tr></table></figure>

<p><strong>添加开机启动RabbitMQ服务</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加开机启动 RabbitMQ服务</span></span><br><span class="line">chkconfig rabbitmq-server on</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动 RabbitMQ服务</span></span><br><span class="line">/sbin/service rabbitmq-server start </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 RabbitMQ服务状态</span></span><br><span class="line">/sbin/service rabbitmq-server status</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">停止 RabbitMQ服务（选择执行）</span></span><br><span class="line">/sbin/service rabbitmq-server stop</span><br></pre></td></tr></table></figure>

<p><strong>开启 web 管理插件</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmq-plugins enable rabbitmq_management</span><br></pre></td></tr></table></figure>

<p><strong>防火墙设置（选择）</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">关闭防火墙</span></span><br><span class="line">systemctl stop firewalld</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看防火墙状态</span></span><br><span class="line">systemctl status firewalld</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下次开机关闭防火墙</span></span><br><span class="line">systemctl enable firewalld</span><br></pre></td></tr></table></figure>

<p><strong>访问RabbitMQ后台</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">可以使用浏览器打开web管理端：http://Server-IP:15672 </span><br></pre></td></tr></table></figure>

<p><strong>添加RabbitMQ后台用户与赋予权限</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加一个新的用户 admin  密码 123</span></span><br><span class="line">rabbitmqctl add_user admin 123</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置用户角色administrator 超级管理员</span></span><br><span class="line">rabbitmqctl set_user_tags admin administrator</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置用户权限 对/ 可读可写</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> set_permissions [-p &lt;vhostpath&gt; ] &lt;user&gt; &lt;conf&gt; &lt;write&gt; &lt;<span class="built_in">read</span>&gt;</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">用户 user_admin 具有/vhost1 这个 virtual host 中所有资源的配置、写、读权限</span></span><br><span class="line">rabbitmqctl set_permissions -p &quot;/&quot; admin &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看当前rabbitmq的后台用户</span></span><br><span class="line">rabbitmqctl list_users</span><br></pre></td></tr></table></figure>

<p><strong>登陆RabbitMQ后台</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">admin 123</span><br></pre></td></tr></table></figure>



<h4 id="Docker安装MQ"><a href="#Docker安装MQ" class="headerlink" title="Docker安装MQ"></a>Docker安装MQ</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">搜索mq镜像版本</span></span><br><span class="line">docker search rabbitmq</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取镜像</span></span><br><span class="line">docker pull rabbitmq</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看镜像</span></span><br><span class="line">docker imgaes</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建并运行容器</span></span><br><span class="line">docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 \</span><br><span class="line">-v /mq/data:/var/lib/rabbitmq  \</span><br><span class="line">--hostname myRabbit  \</span><br><span class="line">rabbitmq</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除容器</span></span><br><span class="line">docker rm -f 080234234232</span><br></pre></td></tr></table></figure>

<p>说明</p>
<blockquote>
<p>-d 后台运行容器；<br>–name 指定容器名；<br>-p 指定服务运行的端口（5672：应用访问端口；15672：控制台Web端口号）；<br>-v 映射目录或文件；<br>–hostname 主机名（RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据，默认为主机名）；</p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看容器</span></span><br><span class="line">docker ps -a</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入容器</span></span><br><span class="line">docker exec -it 564d2be79d62 /bin/bash </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装页面管理插件</span></span><br><span class="line">rabbitmq-plugins enable rabbitmq_management</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">创建admin用户</span></span><br><span class="line">rabbitmqctl add_user admin admin</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加权限 .* 表示最高权限/所有权限</span></span><br><span class="line">rabbitmqctl set_permissions -p &quot;/&quot; admin &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加用户名角色，这里添加为 administrator (系统管理员)</span></span><br><span class="line">rabbitmqctl set_user_tags admin administrator</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">重启rabbitmq</span></span><br><span class="line">docker restart rabbitmq</span><br></pre></td></tr></table></figure>



<p><strong>访问管理后台</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">可以使用浏览器打开web管理端：http://Server-IP:15672 </span><br></pre></td></tr></table></figure>

<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230506235136342.png"></p>
<h3 id="HelloWrold"><a href="#HelloWrold" class="headerlink" title="HelloWrold"></a>HelloWrold</h3><p>用 Java 编写两个程序。发送单个消息的生产者和接收消息并打印出来的消费者</p>
<p>在下图中，“ P” 是我们的生产者，“ C” 是我们的消费者。中间的框是一个队列 RabbitMQ 代表使用者保留的消息缓冲区</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508124653059.png"></p>
<blockquote>
<p>注意</p>
<p>Java 进行连接的时候，需要 Linux 开放 5672 端口，否则会连接超时</p>
<p>访问 Web 界面的端口是 15672，连接服务器的端口是 5672</p>
</blockquote>
<p><strong>步骤</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508124901636.png"></p>
<p><strong>依赖</strong></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--rabbitmq 依赖客户端--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.rabbitmq<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>amqp-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.8.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--操作文件流的一个依赖--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-io<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-io<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.6<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--指定 jdk 编译版本--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">source</span>&gt;</span>8<span class="tag">&lt;/<span class="name">source</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">target</span>&gt;</span>8<span class="tag">&lt;/<span class="name">target</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="生产者代码"><a href="#生产者代码" class="headerlink" title="生产者代码"></a>生产者代码</h4><p>创建一个类作为生产者，最终生产消息到 RabbitMQ 的队列里</p>
<p><strong>生产者步骤</strong></p>
<p>1、创建 RabbitMQ 连接工厂</p>
<p>2、进行 RabbitMQ 工厂配置信息</p>
<p>3、创建 RabbitMQ 连接</p>
<p>4、创建 RabbitMQ 信道</p>
<p>5、生成一个队列</p>
<p>6、发送一个消息到交换机，交换机发送到队列。”” 代表默认交换机</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producter</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">    <span class="comment">//服务器端口号</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String host=<span class="string">&quot;192.168.124.132&quot;</span>;</span><br><span class="line">    <span class="comment">//ribbitmq登录的用户名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String name=<span class="string">&quot;admin&quot;</span>;</span><br><span class="line">    <span class="comment">//ribbitmq登录的用户密码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String password=<span class="string">&quot;123&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="comment">//创建一个连接工厂</span></span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        factory.setHost(host);</span><br><span class="line">        factory.setUsername(name);</span><br><span class="line">        factory.setPassword(password);</span><br><span class="line">        <span class="comment">//创建连接</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        <span class="comment">//获取信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span>connection.createChannel();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 生成(声明)一个队列</span></span><br><span class="line"><span class="comment">         * 1.队列名称</span></span><br><span class="line"><span class="comment">         * 2.队列里面的消息是否持久化(磁盘) 默认true消息存储在内存中</span></span><br><span class="line"><span class="comment">         * 3.该队列是否只供一个消费者进行消费 是否进行共享， true 可以多个消费者消费</span></span><br><span class="line"><span class="comment">         * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除</span></span><br><span class="line"><span class="comment">         * 5.其他参数(Map,死信队列会用到)</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.queueDeclare(QUEUE_NAME,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        String message=<span class="string">&quot;hello world&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 发送一个消息</span></span><br><span class="line"><span class="comment">         * 1.发送到那个交换机(&quot;&quot;代表默认交换机)</span></span><br><span class="line"><span class="comment">         * 2.路由的 key 是哪个(队列名字)</span></span><br><span class="line"><span class="comment">         * 3.其他的参数信息(Map,死信队列会用到)</span></span><br><span class="line"><span class="comment">         * 4.发送消息的消息体(String)</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicPublish(<span class="string">&quot;&quot;</span>,QUEUE_NAME,<span class="literal">null</span>,message.getBytes());</span><br><span class="line">        System.out.println(<span class="string">&quot;消息发送完毕&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结果</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508125749500.png"></p>
<h4 id="消息消费者"><a href="#消息消费者" class="headerlink" title="消息消费者"></a>消息消费者</h4><p>创建一个类作为消费者，消费 RabbitMQ 队列的消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">    <span class="comment">//服务器端口号</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String host=<span class="string">&quot;192.168.124.132&quot;</span>;</span><br><span class="line">    <span class="comment">//ribbitmq登录的用户名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String name=<span class="string">&quot;admin&quot;</span>;</span><br><span class="line">    <span class="comment">//ribbitmq登录的用户密码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String password=<span class="string">&quot;123&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        factory.setHost(host);</span><br><span class="line">        factory.setUsername(name);</span><br><span class="line">        factory.setPassword(password);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建连接</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息....&quot;</span>);</span><br><span class="line">        <span class="comment">// 信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> connection.createChannel();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 推送的消息如何进行消费的接口回调(消费者未成功消费的回调) 这里的写法是某种表达式，写DeliverCallback接口的实现</span></span><br><span class="line">        <span class="comment">// 消费者成功消费的回调</span></span><br><span class="line">        DeliverCallback deliverCallback=(consumerTag, delivery)-&gt;&#123;</span><br><span class="line">            String message= <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());<span class="comment">//获取消息的内容体</span></span><br><span class="line">            System.out.println(message);</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 消费者未成功消费的回调</span></span><br><span class="line">        CancelCallback cancelCallback=(consumerTag)-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消息消费被中断&quot;</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 消费者消费消息</span></span><br><span class="line"><span class="comment">         * 1.消费哪个队列</span></span><br><span class="line"><span class="comment">         * 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答</span></span><br><span class="line"><span class="comment">         * 3.消费者成功消费的回调</span></span><br><span class="line"><span class="comment">         * 4.消费者未成功消费的回调</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicConsume(QUEUE_NAME,<span class="literal">true</span>,deliverCallback,cancelCallback);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508130635119.png"></p>
<p><strong>日志</strong></p>
<p>生产者 (生产者程序运行多次，消费者程序就会消费多次)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">消息发送完毕</span><br></pre></td></tr></table></figure>

<p>消费者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">等待接受消息......</span><br><span class="line">hello world    </span><br></pre></td></tr></table></figure>



<h3 id="RabbitMQ工具类"><a href="#RabbitMQ工具类" class="headerlink" title="RabbitMQ工具类"></a>RabbitMQ工具类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RabbitMqUtils</span> &#123;</span><br><span class="line">    <span class="comment">//得到一个连接的 channel</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Channel <span class="title function_">getChannel</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="comment">//创建一个连接工厂</span></span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        <span class="comment">//服务器端口号</span></span><br><span class="line">        factory.setHost(<span class="string">&quot;192.168.124.132&quot;</span>);</span><br><span class="line">        <span class="comment">//ribbitmq登录的用户名</span></span><br><span class="line">        factory.setUsername(<span class="string">&quot;admin&quot;</span>);</span><br><span class="line">        <span class="comment">//ribbitmq登录的用户密码</span></span><br><span class="line">        factory.setPassword(<span class="string">&quot;123&quot;</span>);</span><br><span class="line">        <span class="comment">//创建连接</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        <span class="comment">//创建信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> connection.createChannel();</span><br><span class="line">        <span class="keyword">return</span> channel;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>生产者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producter</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//创建信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 生成(声明)一个队列</span></span><br><span class="line"><span class="comment">         * 1.队列名称</span></span><br><span class="line"><span class="comment">         * 2.队列里面的消息是否持久化(磁盘) 默认true消息存储在内存中</span></span><br><span class="line"><span class="comment">         * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费</span></span><br><span class="line"><span class="comment">         * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除</span></span><br><span class="line"><span class="comment">         * 5.其他参数</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.queueDeclare(QUEUE_NAME,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        String message=<span class="string">&quot;hello world&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 发送一个消息</span></span><br><span class="line"><span class="comment">         * 1.发送到那个交换机</span></span><br><span class="line"><span class="comment">         * 2.路由的 key 是哪个</span></span><br><span class="line"><span class="comment">         * 3.其他的参数信息</span></span><br><span class="line"><span class="comment">         * 4.发送消息的消息体</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicPublish(<span class="string">&quot;&quot;</span>,QUEUE_NAME,<span class="literal">null</span>,message.getBytes());</span><br><span class="line">        System.out.println(<span class="string">&quot;消息发送完毕&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>消费者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//创建信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息....&quot;</span>);</span><br><span class="line">        <span class="comment">//推送的消息如何进行消费的接口回调(消费者未成功消费的回调) 这里的写法是某种表达式，写DeliverCallback接口的实现类</span></span><br><span class="line">        DeliverCallback deliverCallback=(consumerTag, delivery)-&gt;&#123;</span><br><span class="line">            String message= <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());<span class="comment">//获取消息的内容体</span></span><br><span class="line">            System.out.println(message);</span><br><span class="line">        &#125;;</span><br><span class="line">        CancelCallback cancelCallback=(consumerTag)-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消息消费被中断&quot;</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 消费者消费消息</span></span><br><span class="line"><span class="comment">         * 1.消费哪个队列</span></span><br><span class="line"><span class="comment">         * 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答</span></span><br><span class="line"><span class="comment">         * 3.消费者未成功消费的回调</span></span><br><span class="line"><span class="comment">         * 4.消费者未成功消费的回调</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicConsume(QUEUE_NAME,<span class="literal">true</span>,deliverCallback,cancelCallback);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="工作队列与轮询"><a href="#工作队列与轮询" class="headerlink" title="工作队列与轮询"></a>工作队列与轮询</h3><h4 id="工作队列-Work-Queues"><a href="#工作队列-Work-Queues" class="headerlink" title="工作队列 Work Queues"></a>工作队列 Work Queues</h4><p>Work Queues  是工作队列（又称任务队列）的<strong>主要思想是避免立即执行资源密集型任务</strong>，而不得不等待它完成。相反<strong>我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列</strong>。在后台运行的<strong>工作进程将弹出任务并最终执行作业</strong>。当有多个工作线程时，这些工作线程将一起处理这些任务。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508134309619.png"></p>
<h4 id="轮询消费"><a href="#轮询消费" class="headerlink" title="轮询消费"></a>轮询消费</h4><p>轮询消费消息指的是<strong>轮流</strong>消费消息，即每个工作线程都会获取一个消息进行消费，并且获取的次数按照顺序依次往下轮流。</p>
<p>案例中 <strong>生产者叫做 Task</strong>，<strong>一个消费者就是一个工作线程</strong>，启动两个工作线程消费消息，这个两个<strong>工作线程会以轮询的方式消费队列消息</strong>。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508131829893.png"></p>
<h4 id="消费者轮训消费案例"><a href="#消费者轮训消费案例" class="headerlink" title="消费者轮训消费案例"></a>消费者轮训消费案例</h4><p>消费者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Wrok01</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">      </span><br><span class="line">        DeliverCallback deliverCallback=(consumerTag, message)-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;收到的消息：&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody()));<span class="comment">//获取消息的内容体</span></span><br><span class="line">        &#125;;</span><br><span class="line">        CancelCallback cancelCallback=(consumerTag)-&gt;&#123;</span><br><span class="line">            System.out.println(consumerTag+<span class="string">&quot;消息取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">       System.out.println(<span class="string">&quot;C1等待接收消息....&quot;</span>); </span><br><span class="line">        channel.basicConsume(QUEUE_NAME,<span class="literal">true</span>,deliverCallback,cancelCallback);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>IDEA一个消费者类启动两个线程</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508132918882.png"></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508132936311.png"></p>
<p>启动一个消费者先</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508133006027.png"></p>
<p>修改该类的打印信息后，再运行</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">&quot;c2等待接收消息....&quot;</span>)</span><br></pre></td></tr></table></figure>



<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508133120621.png"></p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Task02</span> &#123;</span><br><span class="line">    <span class="comment">//消息队列名称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 生成(声明)一个队列</span></span><br><span class="line"><span class="comment">         * 1.队列名称</span></span><br><span class="line"><span class="comment">         * 2.队列里面的消息是否持久化(磁盘) 默认true消息存储在内存中</span></span><br><span class="line"><span class="comment">         * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费</span></span><br><span class="line"><span class="comment">         * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除</span></span><br><span class="line"><span class="comment">         * 5.其他参数</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.queueDeclare(QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">//String message=&quot;hello world&quot;;//消息</span></span><br><span class="line">        <span class="comment">//从控制台中接受信息</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.next();</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 发送一个消息</span></span><br><span class="line"><span class="comment">             * 1.发送到那个交换机</span></span><br><span class="line"><span class="comment">             * 2.路由的 key 是哪个</span></span><br><span class="line"><span class="comment">             * 3.其他的参数信息</span></span><br><span class="line"><span class="comment">             * 4.发送消息的消息体</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, QUEUE_NAME, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">            System.out.println(<span class="string">&quot;消息发送完毕：&quot;</span> + message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>结果</strong></p>
<p>消费者发送消息: AA , BB </p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508133541746.png"></p>
<p>消费者c1 接受队列中 ”AA消息“ 并处理</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508133610189.png"></p>
<p>消费者c2 接受队列中 ”BB消息“ 并处理</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508133614081.png"></p>
<blockquote>
<p>结论：一个用户可能有多个消费者线程来接受消息，但是这些</p>
<p>线程不能收到一样的消息，rabbitmq底层中采用轮询接受（类似于nginx的负载均衡），否则用户会收到相同的信息</p>
</blockquote>
<h3 id="RabbitMQ应答与发布"><a href="#RabbitMQ应答与发布" class="headerlink" title="RabbitMQ应答与发布"></a>RabbitMQ应答与发布</h3><p>即：消息应答与发布</p>
<p>​        消费者完成一个任务可能需要一段时间，如果其中一个消费者处理一个长的任务并仅只完成了部分突然它挂掉了，会发生什么情况。<strong>RabbitMQ  一旦向消费者传递了一条消息，便立即将该消息标记为删除</strong>。在这种情况下，突然有个<strong>消费者挂掉了，我们将丢失正在处理的消息。以及后续发送给该消费者的消息，因为它无法接收到</strong>。</p>
<h4 id="自动应答"><a href="#自动应答" class="headerlink" title="自动应答"></a>自动应答</h4><p>​        <strong>消息发送后立即被认为已经传送成功</strong>，这种模式需要在<strong>高吞吐量和数据传输安全性方面做权衡</strong>,因为这种模式如果消息在接收到之前，消费者那边出现连接或者 channel 关闭，那么消息就丢失了,当然另一方面这种模式消费者那边可以传递过载的消息，<strong>没有对传递的消息数量进行限制</strong>，当然这样有可能使得消费者这边由于接收太多还来不及处理的消息，导致这些消息的积压，最终使得内存耗尽，最终这些消费者线程被操作系统杀死，<strong>所以这种模式仅适用在消费者可以高效并以 某种速率能够处理这些消息的情况下使用。</strong></p>
<h4 id="手动应答"><a href="#手动应答" class="headerlink" title="手动应答"></a>手动应答</h4><p>消费者线程在消费完消息后，进行应答，RabbitMQ收到后将对应的消息从信道中删除，手动应答好处是：可以批量应答并且减少网路拥堵</p>
<h5 id="手动应答方法"><a href="#手动应答方法" class="headerlink" title="手动应答方法"></a>手动应答方法</h5><p><code>Channel.basicAck</code> (肯定确认应答)：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第一个参数：消息的标记，</span></span><br><span class="line"><span class="comment"> * 第二个参数：是否批处理，</span></span><br><span class="line"><span class="comment"> * 消费者通知RabbitMQ，消息被成功处理，RabbitMQ将其从队列中删除</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">basicAck(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple);</span><br></pre></td></tr></table></figure>

<p><code>Channel.basicReject</code> (否定确认应答)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment"> * 第一个参数：拒绝消息的标记，</span></span><br><span class="line"><span class="comment"> * 第二个参数：是否重新入队。 true 则重新入队列，false 则丢弃或者进入死信队列。</span></span><br><span class="line"><span class="comment"> * 该方法 reject 后，该消费者还是会消费到该条被 reject 的消息。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">basicReject(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> requeue);</span><br></pre></td></tr></table></figure>

<p><code>Channel.basicNack</code> (用于否定确认)：表示己拒绝处理该消息，可以将其从队列删除</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第一个参数: 拒绝消息的标记，</span></span><br><span class="line"><span class="comment"> * 第二个参数：是否批处理，</span></span><br><span class="line"><span class="comment"> * 第三个参数：是否重新入队，</span></span><br><span class="line"><span class="comment"> * 与 basicReject 区别：</span></span><br><span class="line"><span class="comment"> * 同时支持多个消息，</span></span><br><span class="line"><span class="comment"> * 可以拒绝签收 该消费者先前接收未 ack 的所有消息，拒绝签收后的消息也会被自己消费到。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">basicNack(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple, <span class="type">boolean</span> requeue);</span><br></pre></td></tr></table></figure>

<p>Channel.basicRecover</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 是否恢复消息到队列，</span></span><br><span class="line"><span class="comment"> * 参数是否从新入队，true 则重新入队列，</span></span><br><span class="line"><span class="comment"> * 并且尽可能的将之前 recover 的消息投递给其他消费者消费，而不是自己再次消费。</span></span><br><span class="line"><span class="comment"> * false 则消息会重新被投递给自己。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">basicRecover(<span class="type">boolean</span> requeue);</span><br></pre></td></tr></table></figure>



<h5 id="手动应答的批处理"><a href="#手动应答的批处理" class="headerlink" title="手动应答的批处理"></a>手动应答的批处理</h5><p>multiple 的 true 和 false 代表不同意思，true 代表批量应答。 channel 上未应答的消息</p>
<p>如： channel 上有传送 tag 的消息为 5,6,7,8 ，当前 tag 是8。</p>
<p>multiple为true:   那么此时5-8 的这些还未应答的消息都会被确认（RabbitMQ认为这些tag (5~8) 的消息都被应答 所以在队列中删除）。</p>
<p>multiple为 false : 只会应答 tag=8 的消息 5,6,7 这三个消息依然不会被确认收到消息应答（RabbitMQ认为这些tag=8的消息都被应答 所以在</p>
<p>队列中删除）</p>
<p>如图：</p>
<p><strong>批处理</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508142414724.png"></p>
<p><strong>不批处理</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508142423818.png"></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508143513802.png"></p>
<h5 id="消息自动从新入队"><a href="#消息自动从新入队" class="headerlink" title="消息自动从新入队"></a>消息自动从新入队</h5><p>如果<strong>消费者由于某些原因失去连接</strong>(其通道已关闭，连接已关闭或 TCP 连接丢失)，<strong>导致消息未发送 ACK 确认，RabbitMQ  将了解到消息未完全处理，并将对其重新排队。如果此时其他消费者可以处理，它将很快将其重新分发给另一个消费者</strong>。这样，即使某个消费者偶尔死亡，也可以<strong>确保不会丢失</strong>任何消息。</p>
<h4 id="手动应答案例"><a href="#手动应答案例" class="headerlink" title="手动应答案例"></a>手动应答案例</h4><p>默认消息采用的是自动应答，所以我们要想实现消息消费过程中不丢失，需要把自动应答改为手动应答</p>
<p>消费者启用两个线程，消费 1 一秒消费一个消息，消费者 2 十秒消费一个消息，然后在消费者 2 消费消息的时候，停止运行，这时正在消费的消息是否会重新进入队列，而后给消费者 1 消费呢？</p>
<p><strong>休眠工具类</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SleepUtils</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">sleep</span><span class="params">(<span class="type">int</span> second)</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>*second);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            Thread.currentThread().interrupt();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消费者1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Work031</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ACK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;C1 等待接收消息处理时间较短&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            SleepUtils.sleep(<span class="number">1</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到消息:&quot;</span> + message);</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 下面形参</span></span><br><span class="line"><span class="comment">             * 1.消息标记 tag</span></span><br><span class="line"><span class="comment">             * 2.是否批量应答未应答消息</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//采用手动应答</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">autoAck</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        channel.basicConsume(ACK_QUEUE_NAME, autoAck, deliverCallback, (consumerTag) -&gt; &#123;</span><br><span class="line">            System.out.println(consumerTag + <span class="string">&quot;消费者取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消费者2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Work032</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ACK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;C2 等待接收消息处理时间较长&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            SleepUtils.sleep(<span class="number">30</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到消息:&quot;</span> + message);</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 下面形参</span></span><br><span class="line"><span class="comment">             * 1.消息标记 tag</span></span><br><span class="line"><span class="comment">             * 2.是否批量应答未应答消息</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//采用手动应答</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">autoAck</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        channel.basicConsume(ACK_QUEUE_NAME, autoAck, deliverCallback, (consumerTag) -&gt; &#123;</span><br><span class="line">            System.out.println(consumerTag + <span class="string">&quot;消费者取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消息生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Task03</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TASK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        channel.queueDeclare(TASK_QUEUE_NAME,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;请输入信息：&quot;</span>);</span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> scanner.next();</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>,TASK_QUEUE_NAME,<span class="literal">null</span>,msg.getBytes(<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发送消息：&quot;</span>+msg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">假如work032消费者宕机（模拟-关闭程序）了，手动应答则会将消息放回队列中，交由work031去消费消息</span><br></pre></td></tr></table></figure>

<p><strong>生产者</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508152634625.png"></p>
<p><strong>消费者2</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508152645564.png"></p>
<p><strong>消费者1</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508152745435.png"></p>
<h4 id="发布确认"><a href="#发布确认" class="headerlink" title="发布确认"></a>发布确认</h4><p>生产者将信道设置成 confirm 模式，一旦信道进入 confirm 模式，所有在该信道上面发布的<strong>消息都将会被指派一个唯一的 ID(从1 开始)<strong>，一旦消息被投递到所有匹配的队列之后，</strong>broker就会发送一个确认给生产者(包含消息的唯一 ID)<strong>，这就</strong>使得生产者知道消息已经正确到达目的队列了</strong>，如果消息和队列是可持久化的，那么确认消息会在将消息写入磁盘之后发出，broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号，此外 broker 也可以设置basic.ack 的 multiple 域，表示到这个序列号之前的所有消息都已经得到了处理。</p>
<p>​        confirm 模式最大的好处在于他是异步的，一旦发布一条消息，生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息，当消息最终得到确认之后，生产者应用便可以通过回调方法来处理该确认消息，如果 RabbitMQ 因为自身内部错误导致消息丢失，就会发送一条 nack 消息，生产者应用程序同样可以在回调方法中处理该 nack 消息。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509101704604.png"></p>
<h5 id="发布确认的策略"><a href="#发布确认的策略" class="headerlink" title="发布确认的策略"></a>发布确认的策略</h5><p>发布确认默认是没有开启的，如果要开启需要调用方法 <em>confirmSelect</em>，每当你要想使用发布确认，都需要在 <em>channel</em> 上调用该方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> connection.createChannel();</span><br><span class="line">channel.confirmSelect();</span><br></pre></td></tr></table></figure>



<h5 id="单个确认发布"><a href="#单个确认发布" class="headerlink" title="单个确认发布"></a>单个确认发布</h5><p>​        这是一种简单的确认方式，它是一种<strong>同步确认发布</strong>的方式，也就是发布一个消息之后只有它被确认发布，后续的消息才能继续发布,waitForConfirmsOrDie(long)这个方法只有在消息被确认的时候才返回，如果在指定时间范围内这个消息没有被确认那么它将抛出异常。<br>​        这种确认方式有一个最大的<strong>缺点</strong>就是:<strong>发布速度特别的慢</strong>，因为如果没有确认发布的消息就会阻塞所有后续消息的发布，这种方式最多提供<strong>每秒不超过数百条发布消息的吞吐量</strong>。当然对于某些应用程序来说这可能已经足够了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfirmMessage</span> &#123;</span><br><span class="line">    <span class="comment">//发布的消息数量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Integer</span> <span class="variable">MESSAGE_COUNT</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="comment">//单条确认</span></span><br><span class="line">        publishMessageIndividually();</span><br><span class="line">        <span class="comment">//批量确认</span></span><br><span class="line">        <span class="comment">//多条确认</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">publishMessageIndividually</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">        channel.queueDeclare(queueName, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">//开启发布确认</span></span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Integer</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; MESSAGE_COUNT; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> i + <span class="string">&quot;&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, queueName, <span class="literal">null</span>, message.getBytes());</span><br><span class="line"> 			 <span class="comment">//队列发送确认</span></span><br><span class="line">            <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> channel.waitForConfirms();</span><br><span class="line">            <span class="keyword">if</span> (flag) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;消息发送成功&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;发布&quot;</span> + MESSAGE_COUNT + <span class="string">&quot;个单独确认消息,耗时&quot;</span> + (end - begin) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>确认发布指的是成功发送到了队列，并不是消费者消费了消息。</p>
</blockquote>
<p><strong>效果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509103220597.png"></p>
<p>​        </p>
<h5 id="批量确认发布"><a href="#批量确认发布" class="headerlink" title="批量确认发布"></a>批量确认发布</h5><p>​        单个确认发布那种方式非常慢，与单个等待确认消息相比，<strong>先发布一批消息然后一起确认可以极大地提高吞吐量，当然这种方式的缺点就是:当发生故障导致发布出现问题时，不知道是哪个消息出现问题了</strong>，我们必须将整个批处理保存在内存中，以记录重要的信息而后重新发布消息。当然这种方案仍然是同步的，也一样阻塞消息的发布。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfirmMessage</span> &#123;</span><br><span class="line">    <span class="comment">//发布的消息数量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Integer</span> <span class="variable">MESSAGE_COUNT</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//单条确认</span></span><br><span class="line">        <span class="comment">//publishMessageIndividually();</span></span><br><span class="line">        <span class="comment">//批量确认</span></span><br><span class="line">        publishMessageBatch();</span><br><span class="line">        <span class="comment">//异步确认</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">publishMessageBatch</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">        channel.queueDeclare(queueName, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">//开启发布确认</span></span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        <span class="comment">//确认消息的条数</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">batchSize</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Integer</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; MESSAGE_COUNT; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> i + <span class="string">&quot;&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, queueName, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">            <span class="comment">//队列发送确认 (100条消息就确认一次)</span></span><br><span class="line">            <span class="keyword">if</span> (i % batchSize == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">//确认</span></span><br><span class="line">                channel.waitForConfirms();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;发布&quot;</span> + MESSAGE_COUNT + <span class="string">&quot;批量单独确认消息,耗时&quot;</span> + (end - begin) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509103536225.png"></p>
<h5 id="异步确认发布"><a href="#异步确认发布" class="headerlink" title="异步确认发布"></a>异步确认发布</h5><p>异步确认虽然编程逻辑比上两个要复杂，但是性价比最高，无论是可靠性还是效率都没得说，他是利用回调函数来达到消息可靠性传递的，这个中间件也是通过函数回调来保证是否投递成功。</p>
<p><strong>原理图</strong></p>
<p>采用一个map 去映射消息序号与消息内容</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509104050281.png"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">publishMessageAsync</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">    <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">    channel.queueDeclare(queueName, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="comment">//开启发布确认</span></span><br><span class="line">    channel.confirmSelect();</span><br><span class="line">    <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 确认收到消息的一个回调</span></span><br><span class="line"><span class="comment">     * 1.消息序列号  map的key</span></span><br><span class="line"><span class="comment">     * 2.true 可以确认小于等于当前序列号的消息</span></span><br><span class="line"><span class="comment">     * false 确认当前序列号消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">//收到消息的成功回调函数（消息未有丢失）</span></span><br><span class="line">    ConfirmCallback ackCallback=(deliveryTag, multiple)-&gt;&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;确认的发布消息，序列号&quot;</span>+deliveryTag);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">//未收到消息的回调函数（消息丢失）</span></span><br><span class="line">    ConfirmCallback nackCallback=(deliveryTag, multiple)-&gt;&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;未确认的发布消息，序列号&quot;</span>+deliveryTag);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加一个异步确认的监听器（监听哪些消息成功，哪些消息失败）</span></span><br><span class="line"><span class="comment">     * 1.确认收到消息的回调</span></span><br><span class="line"><span class="comment">     * 2.未收到消息的回调</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    channel.addConfirmListener(ackCallback,nackCallback);<span class="comment">//参数设置为null为不监听</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">Integer</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; MESSAGE_COUNT; i++) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> i + <span class="string">&quot;&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">        channel.basicPublish(<span class="string">&quot;&quot;</span>, queueName, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;发布&quot;</span> + MESSAGE_COUNT + <span class="string">&quot;异步确认消息,耗时&quot;</span> + (end - begin) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509104806837.png"></p>
<h5 id="处理异步未确认消息"><a href="#处理异步未确认消息" class="headerlink" title="处理异步未确认消息"></a>处理异步未确认消息</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">publishMessageAsync</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">    <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">    channel.queueDeclare(queueName, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="comment">//开启发布确认</span></span><br><span class="line">    channel.confirmSelect();</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 线程安全有序的一个哈希表，适用于高并发的情况</span></span><br><span class="line"><span class="comment">     * 1.轻松的将序号与消息进行关联</span></span><br><span class="line"><span class="comment">     * 2.轻松批量删除条目 只要给到序列号</span></span><br><span class="line"><span class="comment">     * 3.支持并发访问</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ConcurrentSkipListMap&lt;Long, String&gt; outstandingConfirms = <span class="keyword">new</span></span><br><span class="line">            <span class="title class_">ConcurrentSkipListMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 确认收到消息的一个回调</span></span><br><span class="line"><span class="comment">     * 1.消息序列号  map的key</span></span><br><span class="line"><span class="comment">     * 2.true 可以确认小于等于当前序列号的消息</span></span><br><span class="line"><span class="comment">     * false 确认当前序列号消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">//收到消息的成功回调函数（消息未有丢失）</span></span><br><span class="line">    ConfirmCallback ackCallback=(deliveryTag, multiple)-&gt;&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;确认的发布消息，序列号&quot;</span>+deliveryTag);</span><br><span class="line">        <span class="keyword">if</span> (multiple) &#123;<span class="comment">//批量处理</span></span><br><span class="line">            <span class="comment">//返回的是小于等于当前序列号的一组【未确认消息】 是一个 map</span></span><br><span class="line">            ConcurrentNavigableMap&lt;Long, String&gt; confirmed =</span><br><span class="line">                    outstandingConfirms.headMap(deliveryTag, <span class="literal">true</span>);</span><br><span class="line">            <span class="comment">//清除该部分未确认消息</span></span><br><span class="line">            confirmed.clear();</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;<span class="comment">//单个</span></span><br><span class="line">            <span class="comment">//只清除当前序列号的消息=全部消息map-确认消息map</span></span><br><span class="line">            outstandingConfirms.remove(deliveryTag);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">//未收到消息的回调函数（消息丢失）</span></span><br><span class="line">    ConfirmCallback nackCallback=(deliveryTag, multiple)-&gt;&#123;</span><br><span class="line"> <span class="comment">//打印未有确认的消息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> outstandingConfirms.get(deliveryTag);</span><br><span class="line">        System.out.println(<span class="string">&quot;未确认的发布消息&quot;</span>+deliveryTag+<span class="string">&quot;，序列号&quot;</span>+deliveryTag);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加一个异步确认的监听器（监听哪些消息成功，哪些消息失败）</span></span><br><span class="line"><span class="comment">     * 1.确认收到消息的回调</span></span><br><span class="line"><span class="comment">     * 2.未收到消息的回调</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    channel.addConfirmListener(ackCallback,nackCallback);<span class="comment">//参数设置为null为不监听</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">Integer</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; MESSAGE_COUNT; i++) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> i + <span class="string">&quot;&quot;</span>;<span class="comment">//消息</span></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * channel.getNextPublishSeqNo()获取下一个消息的序列号</span></span><br><span class="line"><span class="comment">         * 通过序列号与消息体进行一个关联</span></span><br><span class="line"><span class="comment">         * 全部都是未确认的消息体</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        outstandingConfirms.put(channel.getNextPublishSeqNo(),message);</span><br><span class="line">        channel.basicPublish(<span class="string">&quot;&quot;</span>, queueName, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;发布&quot;</span> + MESSAGE_COUNT + <span class="string">&quot;异步确认消息,耗时&quot;</span> + (end - begin) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h5 id="三种确认发布对比"><a href="#三种确认发布对比" class="headerlink" title="三种确认发布对比"></a>三种确认发布对比</h5><ul>
<li><p>单独发布消息</p>
<p>同步等待确认，简单，但吞吐量非常有限。</p>
</li>
<li><p>批量发布消息</p>
<p>批量同步等待确认，简单，合理的吞吐量，一旦出现问题但很难推断出是那条消息出现了问题。</p>
</li>
<li><p>异步处理</p>
<p>最佳性能和资源使用，在出现错误的情况下可以很好地控制，但是实现起来稍微难些</p>
</li>
</ul>
<h4 id="应答和发布的区别"><a href="#应答和发布的区别" class="headerlink" title="应答和发布的区别"></a>应答和发布的区别</h4><p>应答功能属于消费者，消费完消息告诉 RabbitMQ 已经消费成功。</p>
<p>发布功能属于生产者，生产消息到 RabbitMQ，RabbitMQ 需要告诉生产者已经收到消息。</p>
<h3 id="RbbitMQ-持久化"><a href="#RbbitMQ-持久化" class="headerlink" title="RbbitMQ 持久化"></a>RbbitMQ 持久化</h3><p>上面的手动应答可以处理消费者丢失消息的情况，但是<strong>如何保障当 RabbitMQ 服务停掉以后消息生产者发送过来的消息不丢失</strong>。默认情况下 RabbitMQ 退出或由于某种原因崩溃时，它忽视队列和消息，除非告知它不要这样做。<strong>确保消息不会丢失需要做两件事: 我们需要将队列和消息都标记为持久化</strong>。</p>
<h4 id="队列实现持久化"><a href="#队列实现持久化" class="headerlink" title="队列实现持久化"></a>队列实现持久化</h4><p>之前我们创建的队列都是非持久化的，rabbitmg 如果重启的化，该队列就会被删除掉，如果要队列<strong>实现持久化 需要在声明队列的时候把 durable 参数设置为持久化</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Task02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//队列名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TASK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ACK_QUEUE&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开启持久化</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">durable</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">//声明队列</span></span><br><span class="line">        channel.queueDeclare(TASK_QUEUE_NAME,durable,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        <span class="comment">//在控制台中输入信息</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        System.out.println(<span class="string">&quot;请输入信息：&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.next();</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>,TASK_QUEUE_NAME,<span class="literal">null</span>,message.getBytes(<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发出消息:&quot;</span>+ message);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<blockquote>
<p>注意</p>
<p>如果之前声明的队列不是持久化的，需要把原先队列先删除，或者重新创建一个持久化的队列，不然就会出现错误</p>
</blockquote>
<p>不然就会出现如下错误：</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508154754896.png"></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508154822574.png"></p>
<h4 id="消息实现持久化"><a href="#消息实现持久化" class="headerlink" title="消息实现持久化"></a>消息实现持久化</h4><p>需要在<strong>消息生产者</strong>发布消息的时候，开启消息的持久化</p>
<p>在 basicPublish 方法的第二个参数添加这个属性： <code>MessageProperties.PERSISTENT_TEXT_PLAIN</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Task02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//队列名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TASK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ACK_QUEUE&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//队列持久化</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">durable</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">//声明队列</span></span><br><span class="line">        channel.queueDeclare(TASK_QUEUE_NAME,durable,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        <span class="comment">//在控制台中输入信息</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        System.out.println(<span class="string">&quot;请输入信息：&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.next();</span><br><span class="line">        	<span class="comment">//设置生产者发送消息为持久化消息(要求保存到磁盘上)</span></span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>,TASK_QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes(<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发出消息:&quot;</span>+ message);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="RabbitMQ不公分发与预取值"><a href="#RabbitMQ不公分发与预取值" class="headerlink" title="RabbitMQ不公分发与预取值"></a>RabbitMQ不公分发与预取值</h3><h4 id="不公平分发"><a href="#不公平分发" class="headerlink" title="不公平分发"></a>不公平分发</h4><p>在最开始的时候我们学习到 RabbitMQ 分发消息采用的<strong>轮训分发</strong>，但是在某种场景下这种策略并不是很好，<strong>比方说有两个消费者在处理任务，其中有个消费者 1处理任务的速度非常快，而另外一个消费者2处理速度却很慢，这个时候我们还是采用轮训分发的化就会到这处理速度快的这个消费者很大一部分时间处于空闲状态，而处理慢的那个消费者一直在干活，这种分配方式在这种情况下其实就不太好</strong>，但是RabbitMQ 并不知道这种情况它依然很公平的进行分发。</p>
<p>为了避免这种情况，我们可以设置参数 channel.basicQos(1);</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">prefetchCount</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">channel.basicQos(prefetchCount);</span><br></pre></td></tr></table></figure>



<p>​        意思就是如果这个任务我还没有处理完或者我还没有应答你，你先别分配给我，我目前只能处理一个任务，然后rabbitmq 就会把该任务分配给没有那么忙的那个空闲消费者，当然如果所有的消费者都没有完成手上任务，队列还在不停的添加新任务，队列有可能就会遇到队列被撑满的情况，这个时候就只能添加新的 worker 或者改变其他存储任务的策略。</p>
<p><strong>消费者</strong></p>
<p>生产者发送多个消息，消费者中效率高的处理多条消息，效率低的处理少量消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Work032</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ACK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;C2 等待接收消息处理时间较长&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            SleepUtils.sleep(<span class="number">30</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到消息:&quot;</span> + message);</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 下面形参</span></span><br><span class="line"><span class="comment">             * 1.消息标记 tag</span></span><br><span class="line"><span class="comment">             * 2.是否批量应答未应答消息</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//不公平分发为1（效率高，能者多劳），公平分发为0</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">prefetchCount</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        channel.basicQos(prefetchCount);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//采用手动应答</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">autoAck</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        channel.basicConsume(ACK_QUEUE_NAME, autoAck, deliverCallback, (consumerTag) -&gt; &#123;</span><br><span class="line">            System.out.println(consumerTag + <span class="string">&quot;消费者取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>效果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508170139289.png"></p>
<h4 id="预取值"><a href="#预取值" class="headerlink" title="预取值"></a>预取值</h4><p>​        本身消息的发送就是异步发送的，所以在任何时候，channel 上肯定不止只有一个消息另外来自消费者的手动确认本质上也是异步的。因此这里就存在一个未确认的消息缓冲区，因此<strong>希望开发人员能限制此缓冲区的大小，以避免缓冲区里面无限制的未确认消息问题</strong>。这个时候就可以通过使用 basic.qos 方法设置“预取计数”值来完成的。<strong>该值定义通道上允许的未确认消息的最大数量。一旦数量达到配置的数量，RabbitMQ 将停止在通道上传递更多消息，除非至少有一个未处理的消息被确认</strong>，例如，假设在通道上有末确认的消息 5、6、7，8，并且通道的预取计数设置为 4，此时 RabbitMQ 将不会在该通道上再传递任何消息，除非至少有一个未应答的消息被 ak，比方说 tag=6 这个消息刚刚被确认 ACK，RabbitMQ 将会感知这个情况到并再发送一条消息。消息应答和 QoS 预取值对用户吞吐量有重大影响。<strong>通常，增加预取将提高向消费者传递消息的速度</strong>。虽然自动应答传输消息速率是最佳的，但是，在这种情况下已传递但尚未处理</p>
<p>的消息的数量也会增加，从而增加了消费者的 RAM 消耗(随机存取存储器)应该小心使用具有无限预处理的自动确认模式或手动确认模式，<strong>消费者消费了大量的消息如果没有确认的话，会导致消费者连接节点的内存消耗变大</strong>，所以找到合适的预取值是一个反复试验的过程，不同的负载该值取值也不同 100 到 300 范围内的值通常可提供最佳的吞吐量，并且不会给消费者带来太大的风险。预取值为 1 是最保守的。当然这将使吞吐量变得很低，特别是消费者连接延迟很严重的情况下，特别是在消费者连接等待时间较长的环境中。对于大多数应用来说，稍微高一点的值将是最佳的。</p>
<p>效率高的工作线程（消费者）的信道的预期值 设置高点，避免消息在其他信道积压,下面的图就无语（反例子）</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230508171023479.png"></p>
<blockquote>
<p>不公平分发和预取值分发都用到 <code>basic.qos</code> 方法，如果取值为 1，代表不公平分发，取值不为1，代表预取值分发</p>
</blockquote>
<p><strong>消费者1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Work031</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ACK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;C1 等待接收消息处理时间较长&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            SleepUtils.sleep(<span class="number">1</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到消息:&quot;</span> + message);</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 下面形参</span></span><br><span class="line"><span class="comment">             * 1.消息标记 tag</span></span><br><span class="line"><span class="comment">             * 2.是否批量应答未应答消息</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 值不等于 1，则代表预取值,预取值为6</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">prefetchCount</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line">        channel.basicQos(prefetchCount);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//采用手动应答</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">autoAck</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        channel.basicConsume(ACK_QUEUE_NAME, autoAck, deliverCallback, (consumerTag) -&gt; &#123;</span><br><span class="line">            System.out.println(consumerTag + <span class="string">&quot;消费者取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消费者2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Work032</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ACK_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;ack_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;C2 等待接收消息处理时间较长&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            SleepUtils.sleep(<span class="number">30</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到消息:&quot;</span> + message);</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 下面形参</span></span><br><span class="line"><span class="comment">             * 1.消息标记 tag</span></span><br><span class="line"><span class="comment">             * 2.是否批量应答未应答消息</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 值不等于 1，则代表预取值,预取值为2</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">prefetchCount</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">        channel.basicQos(prefetchCount);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//采用手动应答</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">autoAck</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        channel.basicConsume(ACK_QUEUE_NAME, autoAck, deliverCallback, (consumerTag) -&gt; &#123;</span><br><span class="line">            System.out.println(consumerTag + <span class="string">&quot;消费者取消消费接口回调逻辑&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>效果</strong></p>
<p>生产者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">生产者生产多条消息: AA BB CC </span><br></pre></td></tr></table></figure>

<p>消费者们</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">消费者<span class="number">1</span>（效率高）的信道（缓存区）先分配了AA， 消费者<span class="number">2</span>（效率低）的信道（缓存区）分配了BB， 根据预测值将CC 放到消费者<span class="number">1</span>的信道，两者分别去消费。</span><br></pre></td></tr></table></figure>



<h3 id="RabbitMQ交换机"><a href="#RabbitMQ交换机" class="headerlink" title="RabbitMQ交换机"></a>RabbitMQ交换机</h3><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><p>​        RabbitMQ 消息传递模型的核心思想是: <strong>生产者生产的消息从不会直接发送到队列</strong>。实际上，通常生产者甚至都不知道这些消息传递传递到了哪些队列中。<br>​        相反，<strong>生产者只能将消息发送到交换机(exchange)<strong>，</strong>交换机</strong>工作的内容非常简单，一方面它<strong>接收来自生产者的消息</strong>，另一方面<strong>将它们推入队列</strong>。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。</p>
<h4 id="无名exchange"><a href="#无名exchange" class="headerlink" title="无名exchange"></a>无名exchange</h4><p>前面部分我们对exchange无所知，但仍然能够将消息发送到队列。之前能实现的原因是因为我们使用的是默认交换，我们通过空字符串(“”)进行标识。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一个参数空字符为默认交换机</span></span><br><span class="line">channel.basicPublish(<span class="string">&quot;&quot;</span>,<span class="string">&quot;hello&quot;</span>，<span class="literal">null</span>,message.getBytes());</span><br></pre></td></tr></table></figure>

<h4 id="临时队列"><a href="#临时队列" class="headerlink" title="临时队列"></a>临时队列</h4><p>​    之前的章节我们使用的是具有特定名称的队列(还记得 hello 和 ack_queue 吗? )队列的名称我们来说至关重要-我们需要<strong>指定我们的消费者去消费哪个队列的消息</strong>。<br>​    每当我们连接到 RabbitMQ 时，我们都需要一个全新的空队列，为此我们可以创建一个具有随机名称的队列，或者能让服务器为我们选择一个随机队列名称那就更好了。其次<strong>一旦我们断开了消费者的连接，队列将被自动删除</strong>。</p>
<p>创建临时队列的方式如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> channel.queueDecare().getQueue();</span><br></pre></td></tr></table></figure>



<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509113025142.png"></p>
<h4 id="后台交换机绑定队列"><a href="#后台交换机绑定队列" class="headerlink" title="后台交换机绑定队列"></a>后台交换机绑定队列</h4><p>交换机通过routingkey绑定队列，通过key的值来将消费发到指定的队列中去</p>
<p><strong>创建队列</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509113237758.png"></p>
<p><strong>创建交换机</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509113358297.png"></p>
<p><strong>绑定交换机</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509113542776.png"></p>
<p><strong>效果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509113613609.png"></p>
<h4 id="交换机的类型"><a href="#交换机的类型" class="headerlink" title="交换机的类型"></a>交换机的类型</h4><ul>
<li><p><strong>直接(direct)</strong></p>
<p>  ​    处理路由键。需要将一个队列绑定到交换机上，要求该消息与一个特定的<strong>路由键完全匹配</strong>。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键  abc ，则只有被标记为 abc 的消息才被转发，不会转发 abc.def，也不会转发 dog.ghi，只会转发 abc。</p>
</li>
<li><p><strong>主题(topic)</strong></p>
<p>  <strong>将路由键和某模式进行匹配</strong>。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词，符号 * 匹配不多不少一个词。因此 abc.# 能够匹配到 abc.def.ghi，但是 abc.* 只会匹配到 abc.def</p>
</li>
<li><p><strong>标题(headers)</strong></p>
<p>  不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定 Queue 与 Exchange  时指定一组键值对；<strong>当消息发送到RabbitMQ 时会取到该消息的 headers 与 Exchange  绑定时指定的键值对进行匹配</strong>；如果完全匹配则消息会路由到该队列，否则不会路由到该队列。headers 属性是一个键值对，可以是  Hashtable，键值对的值可以是任何类型。而 fanout，direct，topic 的路由键都需要要字符串形式的。</p>
</li>
</ul>
<p>匹配规则 x-match 有下列两种类型：</p>
<p>x-match = all ：表示所有的键值对都匹配才能接受到消息</p>
<p>x-match = any ：表示只要有键值对匹配就能接受到消息</p>
<ul>
<li><p><strong>扇出(fanout)</strong> ：</p>
<pre><code>不处理路由键。你只需要简单的将队列绑定到交换机上。**一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上**。很像子网广播，每台子网内的主机都获得了一份复制的消息。Fanout 交换机转发消息是最快的。
</code></pre>
</li>
</ul>
<h4 id="Fanout-交换机"><a href="#Fanout-交换机" class="headerlink" title="Fanout 交换机"></a>Fanout 交换机</h4><p>为了说明这种模式，我们将构建一个简单的日志系统。它将由两个程序组成:第一个程序将发出日志消息，第二个程序是消费者。其中启动两个消费者，其中一个消费者接收到消息后把日志存储在磁盘</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509120220406.png"></p>
<p><strong>Logs（交换机） 和临时队列的绑定关系</strong></p>
<p>消费者们创建共同交换机（logs），共同交换机设置路由key (fanout ), 分别创建无名队列 (交换机一个routing key: fanout)</p>
<blockquote>
<p>注意</p>
<p>先启动两个消费者再启动生产者。</p>
<p>生产者生产消息后，如果没有对应的消费者接收，则该消息是遗弃的消息</p>
</blockquote>
<p><strong>消费者1</strong></p>
<p>将接收到的消息打印在控制台</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogs02</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="comment">//定义各发布订阅的交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME,<span class="string">&quot;fanout&quot;</span>);</span><br><span class="line">        <span class="comment">//临时消息队列，名字随机，断开连接后自动删除</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> channel.queueDeclare().getQueue();</span><br><span class="line">        <span class="comment">//绑定  队列名字，交换机名字，绑定的key</span></span><br><span class="line">        channel.queueBind(queueName,EXCHANGE_NAME,<span class="string">&quot;&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息,把接收到的消息打印在屏幕.....&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody(), <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;控制台打印接收到的消息&quot;</span>+message);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(queueName,deliverCallback,consumerTag -&gt; &#123; &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消费者2</strong></p>
<p>把消息写出到文件（代码中暂时为打印到控制台）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogs01</span> &#123;</span><br><span class="line">    <span class="comment">//交换机名字</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="comment">//定义各发布订阅的交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME,<span class="string">&quot;fanout&quot;</span>);</span><br><span class="line">        <span class="comment">//临时消息队列，名字随机，断开连接后自动删除</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> channel.queueDeclare().getQueue();</span><br><span class="line">        <span class="comment">//绑定  队列名字，交换机名字，绑定的key</span></span><br><span class="line">        channel.queueBind(queueName,EXCHANGE_NAME,<span class="string">&quot;&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息,把接收到的消息打印在屏幕.....&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody(), <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;控制台打印接收到的消息&quot;</span>+message);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(queueName,deliverCallback,consumerTag -&gt; &#123; &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmitLog</span> &#123;</span><br><span class="line">    <span class="comment">//交换机名字</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMqUtils.getChannel();</span><br><span class="line">        <span class="comment">//声明交换机这里无需声明了，因为消费者中创建了，该交换机已经存在</span></span><br><span class="line">        <span class="comment">//channel.exchangeDeclare(EXCHANGE_NAME, &quot;fanout&quot;);</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.nextLine();</span><br><span class="line">            <span class="comment">//发给 哪个交换机 routingkey 其他 messaeg的字节码</span></span><br><span class="line">            channel.basicPublish(EXCHANGE_NAME, <span class="string">&quot;&quot;</span>, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发出消息&quot;</span> + message);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>效果</strong></p>
<p><strong>生产者发送消息</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">aa</span><br><span class="line">生产者发出消息aa    </span><br></pre></td></tr></table></figure>

<p><strong>消费者1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">等待接收消息，把接收到的消息打印在屏幕.....</span><br><span class="line">控制台打印接收到的消息aa</span><br></pre></td></tr></table></figure>

<p><strong>消费者2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">等待接收消息，把接收到的消息打印在屏幕.....</span><br><span class="line">控制台打印接收到的消息aa</span><br></pre></td></tr></table></figure>



<h4 id="direct-交换机"><a href="#direct-交换机" class="headerlink" title="direct 交换机"></a>direct 交换机</h4><h5 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h5><p>​    <strong>我们希望将日志消息写入磁盘的程序仅接收严重错误(errros)，而不存储哪些警告(warning)或信息(info)日志消息避免浪费磁盘空间</strong>。Fanout 这种交换类型并不能给我们带来很大的灵活性-它只能进行无意识的广播，在这里我们将使用 direct 这种类型来进行替换，这种类型的工作方式是，消息只去到它绑定的routingKey 队列中去。</p>
<p><strong>一个队列可以绑定多个路由key</strong></p>
<p>​        在上面这张图中,我们可以看到X绑定了两个队列,绑定类型是 diret,队列 Q1 绑定键为 orange队列 Q2 绑定键有两个:一个绑定键为 black，另一个绑定键为 green.<br>在这种绑定情况下，生产者发布消息到 exchange 上，绑定键为 orange 的消息会被发布到队列Q1。绑定键为 blackgreen 和的消息会被发布到队列 Q2，其他消息类型的消息将被丢弃。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509151053852.png"></p>
<h5 id="多重绑定"><a href="#多重绑定" class="headerlink" title="多重绑定"></a>多重绑定</h5><p><strong>多个队列可以绑定同一路由</strong></p>
<p>​    当然如果 exchange 的绑定类型是 direct，但是它绑定的多个队列的 key 如果都相同，在这种情况下虽然绑定类型是 direct 但是它表现的就<strong>和 fanout 有点类似了，就跟广播差不多</strong>，如下图所示</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509151859682.png"></p>
<h5 id="Direct实战"><a href="#Direct实战" class="headerlink" title="Direct实战"></a>Direct实战</h5><p>关系</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509152129151.png"></p>
<p>交换机</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509152151798.png"></p>
<p>C1 消费者：绑定 console 队列，routingKey 为 info、warning</p>
<p>C2 消费者：绑定 disk 队列，routingKey 为 error</p>
<p>当生产者生产消息到 <code>direct_logs</code> 交换机里，该交换机会检测消息的 routingKey 条件，然后分配到满足条件的队列里，最后由消费者从队列消费消息。</p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> frx</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2022/7/24  21:59</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DirectLogs</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;direct_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.next();</span><br><span class="line">            channel.basicPublish(EXCHANGE_NAME,<span class="string">&quot;info&quot;</span>,<span class="literal">null</span>,message.getBytes(<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发出消息:&quot;</span>+message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>消费者1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogsDirect01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String EXCHANGE_NAME=<span class="string">&quot;direct_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">        <span class="comment">//声明一个direct交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);</span><br><span class="line">        <span class="comment">//声明一个队列</span></span><br><span class="line">        channel.queueDeclare(<span class="string">&quot;console&quot;</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        channel.queueBind(<span class="string">&quot;console&quot;</span>,EXCHANGE_NAME,<span class="string">&quot;info&quot;</span>);</span><br><span class="line">        channel.queueBind(<span class="string">&quot;console&quot;</span>,EXCHANGE_NAME,<span class="string">&quot;warning&quot;</span>);</span><br><span class="line">        <span class="comment">//接收消息</span></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt; &#123;</span><br><span class="line">          System.out.println(<span class="string">&quot;ReceiveLogsDirect01控制台打印接收到的消息:&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//消费者取消消息时回调接口</span></span><br><span class="line">        channel.basicConsume(<span class="string">&quot;console&quot;</span>,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消费者2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogsDirect02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String EXCHANGE_NAME=<span class="string">&quot;direct_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">        <span class="comment">//声明一个direct交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);</span><br><span class="line">        <span class="comment">//声明一个队列</span></span><br><span class="line">        channel.queueDeclare(<span class="string">&quot;disk&quot;</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        channel.queueBind(<span class="string">&quot;disk&quot;</span>,EXCHANGE_NAME,<span class="string">&quot;error&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//接收消息</span></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt; &#123;</span><br><span class="line">          System.out.println(<span class="string">&quot;ReceiveLogsDirect02控制台打印接收到的消息:&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//消费者取消消息时回调接口</span></span><br><span class="line">        channel.basicConsume(<span class="string">&quot;disk&quot;</span>,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<p><strong>生产者控制台</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">11</span></span><br><span class="line">生产者发出消息:<span class="number">11</span></span><br></pre></td></tr></table></figure>

<p><strong>消费者1控制台</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ReceiveLogsDirecto1控制台打印接收到的消息:11</span><br></pre></td></tr></table></figure>

<p><strong>消费者2控制台</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"> </span><br></pre></td></tr></table></figure>





<h4 id="Topics-交换机"><a href="#Topics-交换机" class="headerlink" title="Topics 交换机"></a>Topics 交换机</h4><h5 id="介绍-1"><a href="#介绍-1" class="headerlink" title="介绍"></a>介绍</h5><p>在上一个小节中，我们改进了日志记录系统。我们没有使用只能进行随意广播的 fanout 交换机，而是使用了 direct 交换机，从而有能实现有选择性地接收日志。</p>
<p>尽管使用 direct 交换机改进了我们的系统，但是它仍然存在局限性——比方说我们想接收的日志类型有 info.base 和 info.advantage，某个队列只想 info.base 的消息，那这个时候direct 就办不到了。这个时候就只能使用 <strong>topic</strong> 类型</p>
<h5 id="Topic-的要求"><a href="#Topic-的要求" class="headerlink" title="Topic 的要求"></a><strong>Topic 的要求</strong></h5><p>发送到类型是 topic 交换机的消息的 routing_key 不能随意写，必须满足一定的要求，它必须是<strong>一个单词列表</strong>，<strong>以点号分隔开</strong>。这些单词可以是任意单词</p>
<p>比如说：”stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit” 这种类型的。</p>
<p>当然这个单词列表最多不能超过 255 个字节。</p>
<p>在这个规则列表中，其中有两个替换符是大家需要注意的：</p>
<ul>
<li>***(星号)可以代替一个位置**</li>
<li><strong>#(井号)可以替代零个或多个位置</strong></li>
</ul>
<h5 id="Topic匹配案例"><a href="#Topic匹配案例" class="headerlink" title="Topic匹配案例"></a>Topic匹配案例</h5><p>下图绑定关系如下：</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509155740461.png"></p>
<ul>
<li><p>Q1–&gt;绑定的是</p>
<ul>
<li>中间带 orange 带 3 个单词的字符串 <code>(*.orange.*)</code></li>
</ul>
</li>
<li><p>Q2–&gt;绑定的是</p>
<ul>
<li><p>最后一个单词是 rabbit 的 3 个单词 <code>(*.*.rabbit)</code></p>
</li>
<li><p>第一个单词是 lazy 的多个单词 <code>(lazy.#)</code></p>
</li>
</ul>
</li>
</ul>
<p>上图是一个队列绑定关系图，我们来看看他们之间数据接收情况是怎么样的</p>
<table>
<thead>
<tr>
<th>例子</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>quick.orange.rabbit</td>
<td>被队列 Q1Q2 接收到</td>
</tr>
<tr>
<td>lazy.orange.elephant</td>
<td>被队列 Q1Q2 接收到</td>
</tr>
<tr>
<td>quick.orange.fox</td>
<td>被队列 Q1 接收到</td>
</tr>
<tr>
<td>lazy.brown.fox</td>
<td>被队列 Q2 接收到</td>
</tr>
<tr>
<td>lazy.pink.rabbit</td>
<td>虽然满足Q2两个绑定但只被队列 Q2 接收一次</td>
</tr>
<tr>
<td>quick.brown.fox</td>
<td>不匹配任何绑定不会被任何队列接收到会被丢弃</td>
</tr>
<tr>
<td>quick.orange.male.rabbit</td>
<td>是四个单词不匹配任何绑定会被丢弃</td>
</tr>
<tr>
<td>lazy.orange.male.rabbit</td>
<td>是四个单词但匹配 Q2</td>
</tr>
</tbody></table>
<p>注意</p>
<blockquote>
<p>当一个队列绑定键是 #，那么这个队列将接收所有数据，就有点像 fanout 了</p>
<p>如果队列绑定键当中没有 # 和 * 出现，那么该队列绑定类型就是 direct 了</p>
</blockquote>
<p><strong>交换机</strong></p>
<img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509160546725.png" alt="image-20230509160546725" style="zoom:67%;" />



<p>生产多个消息到交换机，交换机按照通配符分配消息到不同的队列中，队列由消费者进行消费</p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmitLogTopic</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;topic_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Q1--&gt;绑定的是</span></span><br><span class="line"><span class="comment">         *      中间带 orange 带 3 个单词的字符串(*.orange.*)</span></span><br><span class="line"><span class="comment">         * Q2--&gt;绑定的是</span></span><br><span class="line"><span class="comment">         *      最后一个单词是 rabbit 的 3 个单词(*.*.rabbit)</span></span><br><span class="line"><span class="comment">         *      第一个单词是 lazy 的多个单词(lazy.#)</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        HashMap&lt;String, String&gt; bindingKeyMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;quick.orange.rabbit&quot;</span>, <span class="string">&quot;被队列 Q1Q2 接收到&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;lazy.orange.elephant&quot;</span>, <span class="string">&quot;被队列 Q1Q2 接收到&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;quick.orange.fox&quot;</span>, <span class="string">&quot;被队列 Q1 接收到&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;lazy.brown.fox&quot;</span>, <span class="string">&quot;被队列 Q2 接收到&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;lazy.pink.rabbit&quot;</span>, <span class="string">&quot;虽然满足两个绑定但只被队列 Q2 接收一次&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;quick.brown.fox&quot;</span>, <span class="string">&quot;不匹配任何绑定不会被任何队列接收到会被丢弃&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;quick.orange.male.rabbit&quot;</span>, <span class="string">&quot;是四个单词不匹配任何绑定会被丢弃&quot;</span>);</span><br><span class="line">        bindingKeyMap.put(<span class="string">&quot;lazy.orange.male.rabbit&quot;</span>, <span class="string">&quot;是四个单词但匹配 Q2&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;String,String&gt; bindingKeyEntry : bindingKeyMap.entrySet())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">routingKey</span> <span class="operator">=</span> bindingKeyEntry.getKey();</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> bindingKeyEntry.getValue();</span><br><span class="line"></span><br><span class="line">            channel.basicPublish(EXCHANGE_NAME,routingKey,<span class="literal">null</span>,message.getBytes(<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;生产者发出消息:&quot;</span>+message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消费者C1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogsTopic01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;topic_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//接收消息</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">        <span class="comment">//声明交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);</span><br><span class="line">        <span class="comment">//声明队列</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> <span class="string">&quot;Q1&quot;</span>;</span><br><span class="line">        channel.queueDeclare(queueName,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        channel.queueBind(queueName,EXCHANGE_NAME,<span class="string">&quot;*.orange.*&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;接收队列：&quot;</span>+queueName+<span class="string">&quot;  绑定键:&quot;</span>+message.getEnvelope().getRoutingKey());</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//接收消息</span></span><br><span class="line">        channel.basicConsume(queueName,<span class="literal">true</span>,deliverCallback,consumerTag -&gt;&#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消费者C2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReceiveLogsTopic02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;topic_logs&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//接收消息</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">        <span class="comment">//声明交换机</span></span><br><span class="line">        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);</span><br><span class="line">        <span class="comment">//声明队列</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> <span class="string">&quot;Q2&quot;</span>;</span><br><span class="line">        channel.queueDeclare(queueName,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line">        channel.queueBind(queueName,EXCHANGE_NAME,<span class="string">&quot;*.*.rabbit&quot;</span>);</span><br><span class="line">        channel.queueBind(queueName,EXCHANGE_NAME,<span class="string">&quot;lazy.#&quot;</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息...&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;接收队列：&quot;</span>+queueName+<span class="string">&quot;  绑定键:&quot;</span>+message.getEnvelope().getRoutingKey());</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//接收消息</span></span><br><span class="line">        channel.basicConsume(queueName,<span class="literal">true</span>,deliverCallback,consumerTag -&gt;&#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">生产者发出消息: 是四个单词不匹配任何绑定会被丢弃</span><br><span class="line">生产者发出消息:不匹配任何绑定不会被任何队列接收到会被丢弃</span><br><span class="line">生产者发出消息:被队列 Q1Q2 接收到生产者发出消息:被队列 Q2 接收到</span><br><span class="line">生产者发出消息:被队列 Q102 接收到</span><br><span class="line">生产者发出消息:被队列 Q1 接收到</span><br><span class="line">生产者发出消息: 虽然满足两个绑定但只被队列 <span class="number">02</span> 接收一次</span><br><span class="line">生产者发出消息:是四个单词但匹配 Q2</span><br></pre></td></tr></table></figure>



<p><strong>消费者C1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">等待接收消息...</span><br><span class="line">    </span><br><span class="line">被队列 Q102 接收到</span><br><span class="line">接收队列: Q1 绑定键:lazy.orange.elephant</span><br><span class="line">    </span><br><span class="line">被队列 Q102 接收到</span><br><span class="line">接收队列: Q1 绑定键:quick.orange.rabbit</span><br><span class="line">    </span><br><span class="line">被队列 Q1 接收到</span><br><span class="line">接收队列: <span class="number">01</span> 绑定键:quick.orange.fox</span><br></pre></td></tr></table></figure>



<p><strong>消费者C2</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">等待接收消息...</span><br><span class="line">被队列 Q102 接收到</span><br><span class="line">接收队列: Q2 绑定键: lazy.orange.elephant</span><br><span class="line">    </span><br><span class="line">被队列 Q2 接收到</span><br><span class="line">接收队列: Q2 绑定键:lazy.brown.fox</span><br><span class="line">    </span><br><span class="line">被队列 Q102 接收到</span><br><span class="line">接收队列:Q2 绑定键:quick.orange.rabbit</span><br><span class="line">    </span><br><span class="line">虽然满足两个绑定但只被队列 Q2 接收一次</span><br><span class="line">接收队列:Q2 绑定键:Lazy.pink.rabbit</span><br><span class="line">    </span><br><span class="line">是四个单词但匹配 Q2</span><br><span class="line">接受队列：Q2 绑定键值：lazy.orange.male.rabbit </span><br></pre></td></tr></table></figure>



<h3 id="死信队列"><a href="#死信队列" class="headerlink" title="死信队列"></a>死信队列</h3><h4 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h4><p>​        先从概念解释上搞清楚这个定义，死信，顾名思义就是<strong>无法被消费的消息</strong>，字面意思可以这样理一般来说，producer 将消息投递到broker 或者直接到queue 里了,consumer 从 aueue 取出消息进行消费，但某些时候由于特定的原因导致 queue 中的某些消息无法被消费，这样的消息如果没有续的处理，就变成了死信，有死信自然就有了死信队列。</p>
<h4 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h4><p>1）保证订单业务的消息数据不丢失，需要使用到RabbitMQ 的死信队列机制，当消息消费发生异常时，将消息投入死信队列中</p>
<p>2）用户在商城下单成功并点击去支付后在指定时间未支付时自动失效</p>
<h4 id="死信的来源"><a href="#死信的来源" class="headerlink" title="死信的来源"></a>死信的来源</h4><ul>
<li>消息TTL过期</li>
</ul>
<p>TTL是 Time To Live 的缩写, 也就是生存时间</p>
<ul>
<li>队列达到最大长度</li>
</ul>
<p>队列满了，无法再添加数据到 MQ 中</p>
<ul>
<li>消息被拒绝</li>
</ul>
<p>（basic.reject 或 basic.nack) 并且 requeue = false</p>
<h4 id="死信实战"><a href="#死信实战" class="headerlink" title="死信实战"></a>死信实战</h4><h5 id="消息TTL过期"><a href="#消息TTL过期" class="headerlink" title="消息TTL过期"></a>消息TTL过期</h5><p>交换机的类型是 direct，两个消费者，一个生产者，两个队列：消息队列和死信队列</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509181153362.png"></p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producer</span> &#123;</span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">     	 <span class="comment">//设置普通交换机 消费者已经定义这里就不需要定义了</span></span><br><span class="line">        <span class="comment">//channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);</span></span><br><span class="line">        <span class="comment">//死信消息 设置ttl时间 live to time 单位是ms</span></span><br><span class="line">        AMQP.<span class="type">BasicProperties</span> <span class="variable">properties</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">AMQP</span>.BasicProperties().builder().expiration(<span class="string">&quot;10000&quot;</span>).build();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;<span class="number">11</span> ; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;info&quot;</span>+i;</span><br><span class="line">            channel.basicPublish(NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>,properties,message.getBytes());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<p><strong>消费者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//死信交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;dead_exchange&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;normal_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;dead_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明死信和普通交换机，类型为direct</span></span><br><span class="line">        channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line">        channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明普通队列</span></span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//过期时间 10s 由生产者指定 更加灵活(上面已经指定了，这里就不用了)</span></span><br><span class="line">        <span class="comment">//arguments.put(&quot;x-message-ttl&quot;,10000);</span></span><br><span class="line">        <span class="comment">//正常的队列设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,DEAD_EXCHANGE);<span class="comment">//图中红箭头</span></span><br><span class="line">        <span class="comment">//设置死信routingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line"></span><br><span class="line">        channel.queueDeclare(NORMAL_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,arguments);</span><br><span class="line">        <span class="comment">/////////////////////////////////////////////////////////////////////////</span></span><br><span class="line">        <span class="comment">//声明死信队列</span></span><br><span class="line">        channel.queueDeclare(DEAD_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定普通的交换机与队列</span></span><br><span class="line">        channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定死信的交换机与死信的队列</span></span><br><span class="line">        channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Consumer01接受的消息是：&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(NORMAL_QUEUE,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>先启动消费者 C1，创建出队列，然后停止该 C1 的运行，则 C1 将无法收到队列的消息，无法收到的消息 10 秒后进入死信队列。启动生产者 producer 生产消息</p>
<p><strong>生产者未发送消息</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509190931805.png"></p>
<p><strong>生产者发送了10条消息，此时正常消息队列有10条未消费消息</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509191008799.png"></p>
<p><strong>时间过去10秒，正常队列里面的消息由于没有被消费，消息进入死信队列</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509191045989.png"></p>
<p><strong>消费者C2</strong></p>
<p>消费死信队列的消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;dead_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收死信消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Consumer02接受的消息是：&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(DEAD_QUEUE,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>效果</strong></p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509191512715.png"></p>
<p>消费者2的控制台</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">等待接收死信消息...</span><br><span class="line">Consumer02接受的消息是: info1</span><br><span class="line">Consumer02接受的消息是: info2</span><br><span class="line">Consumer02接受的消息是: info3</span><br><span class="line">Consumer02接受的消息是: info4</span><br><span class="line">Consumer02接受的消息是: info5</span><br><span class="line">Consumer02接受的消息是: info6</span><br><span class="line">Consumer02接受的消息是: info7</span><br><span class="line">Consumer02接受的消息是: info8</span><br><span class="line">Consumer02接受的消息是: info9</span><br><span class="line">Consumer02接受的消息是: info10</span><br></pre></td></tr></table></figure>





<h5 id="死信最大长度"><a href="#死信最大长度" class="headerlink" title="死信最大长度"></a>死信最大长度</h5><p><strong>消费者</strong></p>
<p>1 消息生产者代码去掉 TTL 属性，<code>basicPublish</code> 的第三个参数改为 null</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producer</span> &#123;</span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//死信消息 设置ttl时间 live to time 单位是ms</span></span><br><span class="line">        <span class="comment">//AMQP.BasicProperties properties =</span></span><br><span class="line">        <span class="comment">//        new AMQP.BasicProperties().builder().expiration(&quot;10000&quot;).build();</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;<span class="number">11</span> ; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;info&quot;</span>+i;</span><br><span class="line">            channel.basicPublish(NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>,<span class="literal">null</span>,message.getBytes());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h5 id="私信消息被拒"><a href="#私信消息被拒" class="headerlink" title="私信消息被拒"></a>私信消息被拒</h5><p>C1 消费者修改以下代码(<strong>启动之后关闭该消费者 模拟其接收不到消息</strong>)</p>
<p>这里 设置正常队列长度的限制，例如发送10个消息，6个为正常，4个为死信</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//死信交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;dead_exchange&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;normal_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;dead_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明死信和普通交换机，类型为direct</span></span><br><span class="line">        channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line">        channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明普通队列</span></span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//过期时间 10s 由生产者指定 更加灵活</span></span><br><span class="line">        <span class="comment">//arguments.put(&quot;x-message-ttl&quot;,10000);</span></span><br><span class="line">        <span class="comment">//正常的队列设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,DEAD_EXCHANGE);<span class="comment">//图中红箭头</span></span><br><span class="line">        <span class="comment">//设置死信routingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line">        <span class="comment">//设置正常队列长度的限制，例如发送10个消息，6个为正常，4个为死信</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-max-length&quot;</span>,<span class="number">6</span>);</span><br><span class="line">        </span><br><span class="line">        channel.queueDeclare(NORMAL_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,arguments);</span><br><span class="line">        <span class="comment">/////////////////////////////////////////////////////////////////////////</span></span><br><span class="line">        <span class="comment">//声明死信队列</span></span><br><span class="line">        channel.queueDeclare(DEAD_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定普通的交换机与队列</span></span><br><span class="line">        channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定死信的交换机与死信的队列</span></span><br><span class="line">        channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Consumer01接受的消息是：&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(NORMAL_QUEUE,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意</p>
<p>因为参数改变了，所以需要把原先队列删除</p>
</blockquote>
<p><strong>消费者c2</strong></p>
<p>消费死信队列的消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;dead_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收死信消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Consumer02接受的消息是：&quot;</span>+<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(),<span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(DEAD_QUEUE,<span class="literal">true</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>启动消费者C1，创建出队列，然后停止该 C1 的运行，启动生产者</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509212507208.png"></p>
<p>启动 C2 消费者,消费死信消息</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509212530299.png"></p>
<p><strong>消费者c2的控制台</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">等待接收死信消息..</span><br><span class="line">Consumer02接受的消息是: info1</span><br><span class="line">Consumer02接受的消息是: info2</span><br><span class="line">Consumer02接受的消息是:info3</span><br><span class="line">Consumer02接受的消息是:info4</span><br></pre></td></tr></table></figure>



<h5 id="死信消息被拒"><a href="#死信消息被拒" class="headerlink" title="死信消息被拒"></a>死信消息被拒</h5><p>1、消息生产者代码同上生产者一致</p>
<p>2、需求：消费者 C1 拒收消息 “info5”，开启手动应答</p>
<p><strong>生产者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producer</span> &#123;</span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line">     	 <span class="comment">//设置普通交换机 消费者已经定义这里就不需要定义了</span></span><br><span class="line">        <span class="comment">//channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);</span></span><br><span class="line">        <span class="comment">//死信消息 设置ttl时间 live to time 单位是ms</span></span><br><span class="line">        <span class="comment">//AMQP.BasicProperties properties =</span></span><br><span class="line">        <span class="comment">//        new AMQP.BasicProperties().builder().expiration(&quot;10000&quot;).build();</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;<span class="number">11</span> ; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;info&quot;</span>+i;</span><br><span class="line">            channel.basicPublish(NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>,<span class="literal">null</span>,message.getBytes());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消费者C1</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;normal_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//死信交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;dead_exchange&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NORMAL_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;normal_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEAD_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;dead_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明死信和普通交换机，类型为direct</span></span><br><span class="line">        channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line">        channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明普通队列</span></span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//过期时间 10s 由生产者指定 更加灵活</span></span><br><span class="line">        <span class="comment">//arguments.put(&quot;x-message-ttl&quot;,10000);</span></span><br><span class="line">        <span class="comment">//正常的队列设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,DEAD_EXCHANGE);<span class="comment">//图中红箭头</span></span><br><span class="line">        <span class="comment">//设置死信routingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line">        <span class="comment">//设置正常队列长度的限制，例如发送10个消息，6个为正常，4个为死信</span></span><br><span class="line">        <span class="comment">//arguments.put(&quot;x-max-length&quot;,6);</span></span><br><span class="line"></span><br><span class="line">        channel.queueDeclare(NORMAL_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,arguments);</span><br><span class="line">        <span class="comment">/////////////////////////////////////////////////////////////////////////</span></span><br><span class="line">        <span class="comment">//声明死信队列</span></span><br><span class="line">        channel.queueDeclare(DEAD_QUEUE,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">false</span>,<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定普通的交换机与队列</span></span><br><span class="line">        channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//绑定死信的交换机与死信的队列</span></span><br><span class="line">        channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,<span class="string">&quot;lisi&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;等待接收消息...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag,message) -&gt;&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody(), <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span>(msg.equals(<span class="string">&quot;info5&quot;</span>))&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;Consumer01接受的消息是：&quot;</span>+msg+<span class="string">&quot;： 此消息是被C1拒绝的&quot;</span>);</span><br><span class="line">                <span class="comment">//requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中</span></span><br><span class="line">                channel.basicReject(message.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">            &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;Consumer01接受的消息是：&quot;</span>+msg);</span><br><span class="line">                channel.basicAck(message.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//开启手动应答，也就是关闭自动应答</span></span><br><span class="line">        channel.basicConsume(NORMAL_QUEUE,<span class="literal">false</span>,deliverCallback,consumerTag -&gt; &#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>开启消费者C1，创建出队列，然后停止该 C1 的运行，启动生产者</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509215213815.png"></p>
<p>启动消费者 C1 等待 10 秒之后，死信队列中存在消息</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509215239219.png"></p>
<p>再启动消费者 C2消费死信队列</p>
<p><strong>消费者c1控制台</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">等待接收消息..</span><br><span class="line">Consumer01接受的消息是: info1</span><br><span class="line">Consumer01接受的消息是: info2</span><br><span class="line">Consumer01接受的消息是: info3</span><br><span class="line">Consumer01接受的消息是: info4</span><br><span class="line">Consumer01接受的消息是: info5:此消息是被C1拒绝的</span><br><span class="line">Consumer01接受的消息是: info6</span><br><span class="line">Consumer01接受的消息是: info7</span><br><span class="line">Consumer01接受的消息是:info8</span><br><span class="line">Consumer01接受的消息是: info9</span><br><span class="line">Consumer01接受的消息是: info10</span><br></pre></td></tr></table></figure>



<p><strong>消费者c2控制台</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">等待接收死信消息..</span><br><span class="line">Consumer02接受的消息是:info5</span><br></pre></td></tr></table></figure>

<h3 id="延迟队列"><a href="#延迟队列" class="headerlink" title="延迟队列"></a>延迟队列</h3><h4 id="延迟队列概念："><a href="#延迟队列概念：" class="headerlink" title="延迟队列概念："></a>延迟队列概念：</h4><p>延时队列,<strong>队列内部是有序的</strong>，最重要的特性就体现在它的<strong>延时属性</strong>上，延时队列中的元素是希望 <strong>在指定时间到了以后或之前取出和处理</strong>，简单来说，<strong>延时队列就是用来存放需要在指定时间被处理的 元素的队列</strong>。</p>
<h4 id="延迟队列使用场景："><a href="#延迟队列使用场景：" class="headerlink" title="延迟队列使用场景："></a>延迟队列使用场景：</h4><ol>
<li>订单在十分钟之内未支付则自动取消</li>
<li>新创建的店铺，如果在十天内都没有上传过商品，则自动发送消息提醒</li>
<li>用户注册成功后，如果三天内没有登陆则进行短信提醒</li>
<li>用户发起退款，如果三天内没有得到处理则通知相关运营人员</li>
<li>预定会议后，需要在预定的时间点前十分钟通知各个与会人员参加会议</li>
</ol>
<p>​        这些场景都有一个特点，<strong>需要在某个事件发生之后或者之前的指定时间点完成某一项任务</strong>，如： 发生订单生成事件，在十分钟之后检查该订单支付状态，然后将未支付的订单进行关闭；那我们一直轮询数据，每秒查一次，取出需要被处理的数据，然后处理不就完事了吗？</p>
<p>​        如果数据量比较少，确实可以这样做，比如：对于「如果账单一周内未支付则进行自动结算」这样的需求，  如果对于<strong>时间不是严格限制</strong>，而是宽松意义上的一周，那么每天晚上<strong>跑个定时任务检查</strong>一下所有未支付的账单，确实也是一个可行的方案。但<strong>对于数据量比较大，并且时效性较强的场景</strong>，如：「订单十分钟内未支付则关闭」，短期内未支付的订单数据可能会有很多，活动期间甚至会达到百万甚至千万级别，对这么庞大的数据量仍旧使用<strong>轮询的方式显然是不可取的</strong>，很可能在一秒内无法完成所有订单的检查，同时会<strong>给数据库带来很大压力</strong>，无法满足业务要求而且性能低下。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509222008163.png"></p>
<h4 id="TTL的两种设置"><a href="#TTL的两种设置" class="headerlink" title="TTL的两种设置"></a>TTL的两种设置</h4><p>TTL 是什么呢？TTL 是 RabbitMQ 中一个消息或者队列的属性，表明一条消息或者该队列中的所有消息的最大存活时间，单位是毫秒。</p>
<p>换句话说，如果一条消息设置了 TTL 属性或者进入了设置 TTL 属性的队列，那么这条<strong>消息如果在 TTL 设置的时间内没有被消费，则会成为「死信」</strong>。如果<strong>同时配置了队列的 TTL 和消息的 TTL，那么较小的那个值将会被使用，有两种方式设置 TTL</strong>。</p>
<p><strong>队列设置TTL</strong></p>
<p>在创建队列的时候设置队列的 x-message-ttl 属性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Object&gt; params = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">params.put(<span class="string">&quot;x-message-ttl&quot;</span>,<span class="number">5000</span>);</span><br><span class="line"><span class="keyword">return</span> QueueBuilder.durable(<span class="string">&quot;QA&quot;</span>).withArguments(args).build(); <span class="comment">// QA 队列的最大存活时间位 5000 毫秒</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>消息设置TTL</strong></p>
<p>针对每条消息设置 TTL</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rabbitTemplate.converAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XC&quot;</span>,message,correlationData -&gt; &#123;</span><br><span class="line">    correlationData.getMessageProperties().setExpiration(<span class="string">&quot;5000&quot;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<p><strong>两者区别</strong></p>
<p>如果设置了队列的 TTL  属性，那么一旦消息过期，就会被队列丢弃(如果配置了死信队列被丢到死信队列中)，而第二种方式，消息即使过期，也不一定会被马上丢弃，因为消息是否过期是在即将投递到消费者之前判定的，如果当前队列有严重的消息积压情况，则已过期的消息也许还能存活较长时间，具体看下方案例。</p>
<p>另外，还需要注意的一点是，<strong>如果不设置 TTL，表示消息永远不会过期</strong>，如果将<strong>TTL 设置为 0</strong>，则表示<strong>除非此时可以直接投递该消息到消费者，否则该消息将会被丢弃</strong></p>
<h4 id="整合SpringBoot"><a href="#整合SpringBoot" class="headerlink" title="整合SpringBoot"></a>整合SpringBoot</h4><p>前一小节我们介绍了死信队列，刚刚又介绍了 TTL，至此利用 RabbitMQ  实现延时队列的两大要素已经集齐，接下来只需要将它们进行融合，再加入一点点调味料，延时队列就可以新鲜出炉了。想想看，延时队列，不就是想要消息延迟多久被处理吗，TTL  则刚好能让消息在延迟多久之后成为死信，另一方面，成为死信的消息都会被投递到死信队列里，这样只需要消费者一直消费死信队列里的消息就完事了，因为里面的消息都是希望被立即处理的消息。</p>
<p>1、创建一个 Maven 工程或者 Spring Boot工程</p>
<p>2、添加依赖，这里的 Spring Boot 是2.5.5 版本</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.5.5<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>fastjson<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.47<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--RabbitMQ 依赖--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-amqp<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>3、创建 <code>application.yml</code> 文件</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8888</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="number">192.168</span><span class="number">.91</span><span class="number">.200</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">5672</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="number">123</span></span><br></pre></td></tr></table></figure>

<p>这里是 8808 端口，可根据需求决定端口</p>
<p>4、新建主启动类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RabbitmqSpringbootApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(RabbitmqSpringbootApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="队列TTL"><a href="#队列TTL" class="headerlink" title="队列TTL"></a>队列TTL</h4><h5 id="代码架构图"><a href="#代码架构图" class="headerlink" title="代码架构图"></a>代码架构图</h5><p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230509225542592.png"></p>
<p>原先配置队列信息，写在了生产者和消费者代码中，现在可写在配置类中，生产者只发消息，消费者只接受消息</p>
<p><strong>构建关系</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TtlQueueConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String X_EXCHANGE=<span class="string">&quot;X&quot;</span>;</span><br><span class="line">    <span class="comment">//死信交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String Y_DEAD_LETTER_EXCHANGE=<span class="string">&quot;Y&quot;</span>;</span><br><span class="line">    <span class="comment">//普通队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String QUEUE_A=<span class="string">&quot;QA&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String QUEUE_B=<span class="string">&quot;QB&quot;</span>;</span><br><span class="line">    <span class="comment">//死信队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEAD_LETTER_QUEUE=<span class="string">&quot;QD&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明xExchange  别名</span></span><br><span class="line">    <span class="meta">@Bean(&quot;xExchange&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">xExchange</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectExchange</span>(X_EXCHANGE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明yExchange 别名</span></span><br><span class="line">    <span class="meta">@Bean(&quot;yExchange&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">yExchange</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectExchange</span>(Y_DEAD_LETTER_EXCHANGE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明普通队列  要有ttl 为10s</span></span><br><span class="line">    <span class="meta">@Bean(&quot;queueA&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">queueA</span><span class="params">()</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">3</span>);</span><br><span class="line">        <span class="comment">//设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,Y_DEAD_LETTER_EXCHANGE);</span><br><span class="line">        <span class="comment">//设置死信RoutingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;YD&quot;</span>);</span><br><span class="line">        <span class="comment">//设置TTL 10s 单位是ms</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-message-ttl&quot;</span>,<span class="number">10000</span>);</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(QUEUE_A).withArguments(arguments).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明普通队列  要有ttl 为40s</span></span><br><span class="line">    <span class="meta">@Bean(&quot;queueB&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">queueB</span><span class="params">()</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">3</span>);</span><br><span class="line">        <span class="comment">//设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,Y_DEAD_LETTER_EXCHANGE);</span><br><span class="line">        <span class="comment">//设置死信RoutingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;YD&quot;</span>);</span><br><span class="line">        <span class="comment">//设置TTL 10s 单位是ms</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-message-ttl&quot;</span>,<span class="number">40000</span>);</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(QUEUE_B).withArguments(arguments).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明死信队列  要有ttl 为40s</span></span><br><span class="line">    <span class="meta">@Bean(&quot;queueD&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">queueD</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(DEAD_LETTER_QUEUE).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明队列 QA 绑定 X 交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueABindingX</span><span class="params">(<span class="meta">@Qualifier(&quot;queueA&quot;)</span> Queue queueA,</span></span><br><span class="line"><span class="params">                                  <span class="meta">@Qualifier(&quot;xExchange&quot;)</span> DirectExchange xExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(queueA).to(xExchange).with(<span class="string">&quot;XA&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明队列 QB 绑定 X 交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueBBindingX</span><span class="params">(<span class="meta">@Qualifier(&quot;queueB&quot;)</span> Queue queueB,</span></span><br><span class="line"><span class="params">                                  <span class="meta">@Qualifier(&quot;xExchange&quot;)</span> DirectExchange xExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(queueB).to(xExchange).with(<span class="string">&quot;XB&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明队列 QD 绑定 Y 交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueDBindingY</span><span class="params">(<span class="meta">@Qualifier(&quot;queueD&quot;)</span> Queue queueD,</span></span><br><span class="line"><span class="params">                                  <span class="meta">@Qualifier(&quot;yExchange&quot;)</span> DirectExchange yExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(queueD).to(yExchange).with(<span class="string">&quot;YD&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>生产者</strong></p>
<p><strong>Controller 层代码，获取消息，放到 RabbitMQ</strong> 里</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/ttl&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SendMsgController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line">    <span class="comment">//开始发消息</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/sendMsg/&#123;message&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMsg</span><span class="params">(<span class="meta">@PathVariable(&quot;message&quot;)</span> String message)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;当前时间:&#123;&#125;,发送一条信息给两个TTL队列：&#123;&#125;&quot;</span>,<span class="keyword">new</span> <span class="title class_">Date</span>().toString(),message);</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        参数： 路由名称，路由key,message</span></span><br><span class="line"><span class="comment">        convertAndSend(…) 使用此方法，交换机会马上把所有的信息都交给所有的消费者，消费者再自行处理，</span></span><br><span class="line"><span class="comment">        不会因为消费者处理慢而阻塞线程。</span></span><br><span class="line"><span class="comment">        返回结果是：消息来自ttl为xx s的队列+message</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XA&quot;</span>,<span class="string">&quot;消息来自ttl为10s的队列:&quot;</span>+message);</span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XB&quot;</span>,<span class="string">&quot;消息来自ttl为40s的队列:&quot;</span>+message);</span><br><span class="line">		</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>测试</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">发起一个请求：<span class="attr">http</span>:<span class="comment">//localhost:8888/ttl/sendMsg/嘻嘻嘻</span></span><br></pre></td></tr></table></figure>

<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511195931570.png"></p>
<p>第一条消息在 10S 后变成了死信消息，然后被消费者消费掉，第二条消息在 40S 之后变成了死信消息， 然后被消费掉，这样一个延时队列就打造完成了。</p>
<p>不过，如果这样使用的话，岂不是每增加一个新的时间需求，就要新增一个队列，这里只有 10S 和 40S 两个时间选项，如果需要一个小时后处理，那么就需要增加 TTL  为一个小时的队列，如果是预定会议室然后提前通知这样的场景，岂不是要增加无数个队列才能满足需求？</p>
<h4 id="延时队列TTL优化"><a href="#延时队列TTL优化" class="headerlink" title="延时队列TTL优化"></a>延时队列TTL优化</h4><p>在这里新增了一个队列 QC，该队列不设置 TTL 时间，根据前端的请求确定 TTL 时间，绑定关系如下：</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511210128115.png"></p>
<p><strong>配置类代码</strong></p>
<p>新增一个配置文件类，用于新增队列 QC，也可以放在上方的配置文件类里</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MsgTtlQueueConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//普通队列的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUEUE_C</span> <span class="operator">=</span> <span class="string">&quot;QC&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//死信交换机的名称</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String Y_DEAD_LETTER_EXCHANGE=<span class="string">&quot;Y&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明QC</span></span><br><span class="line">    <span class="meta">@Bean(&quot;queueC&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">QueueC</span><span class="params">()</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">3</span>);</span><br><span class="line">        <span class="comment">//设置死信交换机</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-exchange&quot;</span>,Y_DEAD_LETTER_EXCHANGE);</span><br><span class="line">        <span class="comment">//设置死信RoutingKey</span></span><br><span class="line">        arguments.put(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>,<span class="string">&quot;XC&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(QUEUE_C).withArguments(arguments).build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//声明队列 QC 绑定 X 交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueCBindingX</span><span class="params">(<span class="meta">@Qualifier(&quot;queueC&quot;)</span> Queue queueC,</span></span><br><span class="line"><span class="params">                                  <span class="meta">@Qualifier(&quot;xExchange&quot;)</span>DirectExchange xExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(queueC).to(xExchange).with(<span class="string">&quot;XC&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>生产者</strong></p>
<p><strong>Controller 新增方法</strong></p>
<p>该方法接收的请求要带有 TTL 时间</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/ttl&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SendMsgController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line">    <span class="comment">//开始发消息</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/sendMsg/&#123;message&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMsg</span><span class="params">(<span class="meta">@PathVariable(&quot;message&quot;)</span> String message)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;当前时间:&#123;&#125;,发送一条信息给两个TTL队列：&#123;&#125;&quot;</span>,<span class="keyword">new</span> <span class="title class_">Date</span>().toString(),message);</span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XA&quot;</span>,<span class="string">&quot;消息来自ttl为10s的队列:&quot;</span>+message);</span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XB&quot;</span>,<span class="string">&quot;消息来自ttl为40s的队列:&quot;</span>+message);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//开始发消息 发TTL</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/sendExpirationMsg/&#123;message&#125;/&#123;ttlTime&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMsg</span><span class="params">(<span class="meta">@PathVariable(&quot;message&quot;)</span> String message,</span></span><br><span class="line"><span class="params">                        <span class="meta">@PathVariable(&quot;ttlTime&quot;)</span> String ttlTime)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;当前时间:&#123;&#125;,发送一条时长是&#123;&#125;毫秒TTL信息给队列QC：&#123;&#125;&quot;</span>,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Date</span>().toString(),ttlTime,message);</span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;X&quot;</span>,<span class="string">&quot;XC&quot;</span>,message,msg -&gt; &#123;</span><br><span class="line">            <span class="comment">//发送消息的时候的延迟时长</span></span><br><span class="line">            msg.getMessageProperties().setExpiration(ttlTime);</span><br><span class="line">            <span class="keyword">return</span> msg;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>重启下面，发送请求:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:8888/ttl/sendExpirationMsg/你好1/20000</span></span><br><span class="line"></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:8888/ttl/sendExpirationMsg/你好2/2000</span></span><br></pre></td></tr></table></figure>

<p><strong>出现问题</strong>:</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511210516564.png"></p>
<p>看起来似乎没什么问题，但是在最开始的时候，就介绍过如果使用在消息属性上设置 TTL 的方式，消息可能并不会按时「死亡」</p>
<blockquote>
<p>因为 RabbitMQ 只会检查第一个消息是否过期，如果过期则丢到死信队列， 如果第一个消息的延时时长很长，而第二个消息的延时时长很短，第二个消息并不会优先得到执行</p>
</blockquote>
<p>这也就是为什么如图的时间：你好 2 延时 2 秒，却后执行，还要等待你好 1 消费后再执行你好2</p>
<h4 id="RabbitMQ插件实现延迟队列"><a href="#RabbitMQ插件实现延迟队列" class="headerlink" title="RabbitMQ插件实现延迟队列"></a>RabbitMQ插件实现延迟队列</h4><p>上文中提到的问题，确实是一个问题，如果不能实现在消息粒度上的 TTL，并使其在设置的 TTL 时间及时死亡，就无法设计成一个通用的延时队列。那如何解决呢，接下来我们就去解决该问题</p>
<p><strong>安装延时队列插件</strong></p>
<p>可去 <a target="_blank" rel="noopener" href="https://www.rabbitmq.com/community-plugins.html">官网下载</a>  找到 <strong>rabbitmq_delayed_message_exchange</strong> 插件，放置到 RabbitMQ 的插件目录。</p>
<p>因为官网也是跳转去该插件的 GitHub 地址进行下载：<a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases">点击跳转</a></p>
<p>打开 Linux，用 <code>Xftp</code> 将插件放到 RabbitMQ 的安装目录下的 plgins 目录，</p>
<p>RabbitMQ 与其 plgins 目录默认分别位于</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">RabbitMQ 安装目录</span></span><br><span class="line">cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.8   </span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">RabbitMQ 的 plgins 所在目录</span></span><br><span class="line">cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.8/plugins</span><br></pre></td></tr></table></figure>

<p>其中我的版本是 <code>/rabbitmq_server-3.8.8</code></p>
<p>进入目录后执行下面命令让该<strong>插件生效</strong>，然后<strong>重启 RabbitMQ</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装</span></span><br><span class="line">rabbitmq-plugins enable rabbitmq_delayed_message_exchange</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">重启服务</span></span><br><span class="line">systemctl restart rabbitmq-server</span><br></pre></td></tr></table></figure>

<p>效果如下</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[root@master plugins]# rabbitmq-plugins enable rabbitmq_delayed_message_exchange</span><br><span class="line">Enabling plugins on node rabbit@master:</span><br><span class="line">rabbitmq_delayed_message_exchange</span><br><span class="line">The following plugins have been configured:</span><br><span class="line">  rabbitmq_delayed_message_exchange</span><br><span class="line">  rabbitmq_management</span><br><span class="line">  rabbitmq_management_agent</span><br><span class="line">  rabbitmq_web_dispatch</span><br><span class="line">Applying plugin configuration to rabbit@master...</span><br><span class="line">The following plugins have been enabled:</span><br><span class="line">  rabbitmq_delayed_message_exchange</span><br><span class="line"></span><br><span class="line">started 1 plugins.</span><br><span class="line">[root@master plugins]# systemctl restart rabbitmq-server</span><br></pre></td></tr></table></figure>

<blockquote>
<p>解释</p>
<p>安装命令不能出现插件版本和后缀，如 <code>rabbitmq-plugins enable rabbitmq_delayed_message_exchange-3.8.0.ez</code> 会报错</p>
<p>必须是 <code>rabbitmq-plugins enable rabbitmq_delayed_message_exchange</code>，后面不允许填入版本和文件后缀</p>
</blockquote>
<p>打开 Web 界面，查看交换机的新增功能列表，如果多出了如图所示，代表成功添加插件</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511211948844.png"></p>
<h4 id="插件实战"><a href="#插件实战" class="headerlink" title="插件实战"></a>插件实战</h4><p>在这里新增了一个队列 delayed.queue，一个自定义交换机 delayed.exchange，绑定关系如下:</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511212109573.png"></p>
<p><strong>配置类代码</strong></p>
<p>新增一个配置类 <code>DelayedQueueConfig</code>，也可以放在原来的配置文件里，代码里使用了 <code>CustomExchange</code> 类，通过参数来自定义一个类型(direct、topic等)</p>
<p>在我们自定义的交换机中，这是一种新的交换类型，该类型消息支持延迟投递机制消息传递后并不会立即投递到目标队列中，而是存储在 mnesia(一个分布式数据系统)表中，当达到投递时间时，才投递到目标队列中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DelayedQueueConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELAYED_EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;delayed.exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELAYED_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;delayed.queue&quot;</span>;</span><br><span class="line">    <span class="comment">//routingKey</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELAYED_ROUTING_KEY</span> <span class="operator">=</span> <span class="string">&quot;delayed.routingkey&quot;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">delayedQueue</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Queue</span>(DELAYED_QUEUE_NAME);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明交换机,基于插件的交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> CustomExchange <span class="title function_">delayedExchange</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; arguments = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        arguments.put(<span class="string">&quot;x-delayed-type&quot;</span>,<span class="string">&quot;direct&quot;</span>);</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 1.交换机的名称</span></span><br><span class="line"><span class="comment">         * 2.交换机的类型 x-delayed-message</span></span><br><span class="line"><span class="comment">         * 3.是否需要持久化</span></span><br><span class="line"><span class="comment">         * 4.是否需要自动删除</span></span><br><span class="line"><span class="comment">         * 5.其他的参数</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CustomExchange</span>(DELAYED_EXCHANGE_NAME,<span class="string">&quot;x-delayed-message&quot;</span>,</span><br><span class="line">                <span class="literal">true</span>,<span class="literal">false</span>,arguments);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绑定</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">delayedQueueBindingDelayedExchange</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@Qualifier(&quot;delayedQueue&quot;)</span> Queue delayedQueue,</span></span><br><span class="line"><span class="params">            <span class="meta">@Qualifier(&quot;delayedExchange&quot;)</span>CustomExchange delayedExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(delayedQueue).to(delayedExchange)</span><br><span class="line">                .with(DELAYED_ROUTING_KEY).noargs();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>生产者代码</strong></p>
<p>在 controller 里新增一个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//开始发消息，基于插件的 消息及 延迟的时间</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/sendDelayMsg/&#123;message&#125;/&#123;delayTime&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMsg</span><span class="params">(<span class="meta">@PathVariable(&quot;message&quot;)</span> String message,</span></span><br><span class="line"><span class="params">                    <span class="meta">@PathVariable(&quot;delayTime&quot;)</span> Integer delayTime)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;当前时间:&#123;&#125;,发送一条时长是&#123;&#125;毫秒TTL信息给延迟队列delayed.queue：&#123;&#125;&quot;</span>,</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Date</span>().toString(),delayTime,message);</span><br><span class="line"></span><br><span class="line">    rabbitTemplate.convertAndSend(DelayedQueueConfig.DELAYED_EXCHANGE_NAME,</span><br><span class="line">            DelayedQueueConfig.DELAYED_ROUTING_KEY,message, msg -&gt; &#123;</span><br><span class="line">        <span class="comment">//发送消息的时候的延迟时长 单位ms</span></span><br><span class="line">        msg.getMessageProperties().setDelay(delayTime);</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消费者代码</strong></p>
<p>监听延时队列，如果有消息进入该队列，则打印到控制台</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DelayQueueConsumer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RabbitListener(queues = DelayedQueueConfig.DELAYED_QUEUE_NAME)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receiveDelayQueue</span><span class="params">(Message message)</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">        log.info(<span class="string">&quot;当前时间：&#123;&#125;,收到延时队列的消息：&#123;&#125;&quot;</span>, <span class="keyword">new</span> <span class="title class_">Date</span>().toString(), msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8888/ttl/sendDelayMsg/hello1/20000</span><br><span class="line"></span><br><span class="line">http://localhost:8888/ttl/sendDelayMsg/hello2/2000</span><br></pre></td></tr></table></figure>

<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511212626180.png"></p>
<p>可以看到哪怕 hello1 需要20秒再进入延时队列，hello2 2 秒后直接进入延时队列，无需等待 hello1</p>
<h3 id="发布确认高级"><a href="#发布确认高级" class="headerlink" title="发布确认高级"></a>发布确认高级</h3><p>在<strong>生产环境中由于一些不明原因，导致 RabbitMQ 重启</strong>，<strong>在 RabbitMQ 重启期间生产者消息投递失败，导致消息丢失，需要手动处理和恢复</strong>。于是，我们开始思考，如何才能进行 RabbitMQ 的消息可靠投递呢？</p>
<h4 id="发布确认SpringBoot版本"><a href="#发布确认SpringBoot版本" class="headerlink" title="发布确认SpringBoot版本"></a>发布确认SpringBoot版本</h4><p>简单的发布确认机制在应答与签收已经介绍，本内容将介绍整合了 SpringBoot 的发布确认机制。</p>
<h4 id="介绍-2"><a href="#介绍-2" class="headerlink" title="介绍"></a>介绍</h4><p>首先<strong>发布消息后进行备份在缓存</strong>里，如果<strong>消息成功发布确认到交换机，则从缓存里删除该消息，如果没有成功发布，则设置一个定时任务，重新从缓存里获取消息发布到交换机，直到成功发布到交换机</strong>。</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511213702967.png"></p>
<h4 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h4><p>一个交换机：confirm.exchange，一个队列：confirm.queue，一个消费者：confirm.consumer</p>
<p>其中交换机类型时 direct，与队列关联的 routingKey 是 key1</p>
<p>代码架构图：</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511213929024.png"></p>
<p>在配置文件当中需要添加：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8888</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="number">192.168</span><span class="number">.91</span><span class="number">.200</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">5672</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="number">123</span></span><br><span class="line">    <span class="attr">publisher-confirm-type:</span> <span class="string">correlated</span></span><br></pre></td></tr></table></figure>
<p>publisher-confirm-type</p>
<ul>
<li><code>NONE</code> 值是禁用发布确认模式，是默认值</li>
<li><code>CORRELATED</code> 值是发布消息成功到交换器后会触发回调方法</li>
<li><code>SIMPLE</code> 值经测试有两种效果，其一效果和 CORRELATED 值一样会触发回调方法，其二在发布消息成功后使用 rabbitTemplate 调用  waitForConfirms 或 waitForConfirmsOrDie 方法等待 broker  节点返回发送结果，根据返回结果来判定下一步的逻辑，要注意的点是 waitForConfirmsOrDie 方法如果返回 false 则会关闭  channel，则接下来无法发送消息到 broker;</li>
</ul>
<p><strong>添加配置类</strong></p>
<p>声明交换机和队列，并且将交换机和队列进行绑定</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfirmConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;confirm_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;confirm_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//routingKey</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_ROUTING_KEY</span> <span class="operator">=</span> <span class="string">&quot;key1&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明交换机</span></span><br><span class="line">    <span class="meta">@Bean(&quot;confirmExchange&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">confirmExchange</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectExchange</span>(CONFIRM_EXCHANGE_NAME);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明队列</span></span><br><span class="line">    <span class="meta">@Bean(&quot;confirmQueue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">confirmQueue</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绑定</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueBindingExchange</span><span class="params">(<span class="meta">@Qualifier(&quot;confirmQueue&quot;)</span> Queue confirmQueue,</span></span><br><span class="line"><span class="params">                                        <span class="meta">@Qualifier(&quot;confirmExchange&quot;)</span> DirectExchange confirmExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_ROUTING_KEY);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>消息生产者</strong></p>
<p>也可以说是 Controller 层</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/confirm&quot;)</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProductController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//开始发消息,测试确认</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/sendMessage/&#123;message&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(<span class="meta">@PathVariable(&quot;message&quot;)</span> String message)</span>&#123;</span><br><span class="line">        <span class="comment">//指定消息 id 为 1</span></span><br><span class="line">        <span class="type">CorrelationData</span> <span class="variable">correlationData1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CorrelationData</span>(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,</span><br><span class="line">                ConfirmConfig.CONFIRM_ROUTING_KEY,message+<span class="string">&quot;key1&quot;</span>,correlationData1);</span><br><span class="line">        log.info(<span class="string">&quot;发送消息内容:&#123;&#125;&quot;</span>,message+<span class="string">&quot;key1&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//指定消息 id 为 2</span></span><br><span class="line">        <span class="type">CorrelationData</span> <span class="variable">correlationData2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CorrelationData</span>(<span class="string">&quot;2&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">CONFIRM_ROUTING_KEY</span> <span class="operator">=</span> <span class="string">&quot;key2&quot;</span>;</span><br><span class="line">        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,</span><br><span class="line">                CONFIRM_ROUTING_KEY,message+<span class="string">&quot;key2&quot;</span>,correlationData2);</span><br><span class="line">        log.info(<span class="string">&quot;发送消息内容:&#123;&#125;&quot;</span>,message+<span class="string">&quot;key2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消息消费者</strong></p>
<p>监听 <code>confirm.queue</code> 队列</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Consumer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RabbitListener(queues = ConfirmConfig.CONFIRM_QUEUE_NAME)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receiveConfirmMessage</span><span class="params">(Message message)</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">        log.info(<span class="string">&quot;接受到的队列confirm.queue消息:&#123;&#125;&quot;</span>,msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消息生产者发布消息后的回调接口</strong></p>
<p>只要生产者发布消息，交换机不管是否收到消息，都会调用该类的 <code>confirm</code> 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 回调接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyCallBack</span> <span class="keyword">implements</span> <span class="title class_">RabbitTemplate</span>.ConfirmCallback &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//注入</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">//注入</span></span><br><span class="line">        rabbitTemplate.setConfirmCallback(<span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 交换机不管是否收到消息的一个回调方法</span></span><br><span class="line"><span class="comment">     * 1. 发消息 交换机接收到了 回调</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> correlationData  保存回调信息的Id及相关信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ack              交换机收到消息 为true</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cause            未收到消息的原因</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">confirm</span><span class="params">(CorrelationData correlationData, <span class="type">boolean</span> ack,String cause)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> correlationData!=<span class="literal">null</span>?correlationData.getId():<span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span>(ack)&#123;</span><br><span class="line">            log.info(<span class="string">&quot;交换机已经收到了ID为:&#123;&#125;的消息&quot;</span>,id);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;交换机还未收到ID为:&#123;&#125;的消息，由于原因:&#123;&#125;&quot;</span>,id,cause);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8888/confirm/sendMessage/大家好1</span><br></pre></td></tr></table></figure>

<p>结果分析:</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511220439604.png"></p>
<p>可以看到，发送了两条消息，第一条消息的 RoutingKey 为 “key1”，第二条消息的 RoutingKey 为  “key2”，两条消息都成功被交换机接收，也收到了交换机的确认回调，但消费者只收到了一条消息，因为<strong>第二条消息的 RoutingKey 与队列的  BindingKey 不一致，也没有其它队列能接收这个消息，所有第二条消息被直接丢弃了</strong>。</p>
<p>丢弃的消息交换机是不知道的，需要解决告诉生产者消息传送失败。</p>
<h4 id="回退消息"><a href="#回退消息" class="headerlink" title="回退消息"></a>回退消息</h4><p><strong>介绍</strong></p>
<p>获取回退的消息，首先在配置文件开启该功能，然后需要自定义类实现 <code>RabbitTemplate.ReturnsCallback</code> 接口，并且初始化时，使用该自定义类作为回退消息的处理类，同时开启 <code>Mandatory</code>，设置为 true</p>
<p>在启动开启 Mandatory，或者在代码里手动开启 Mandatory 参数，或者都开启</p>
<p><strong>开启方式两种：</strong></p>
<p>配置类文件开启：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 新版</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">  	<span class="attr">template:</span></span><br><span class="line">      <span class="attr">mandatory:</span> <span class="literal">true</span></span><br><span class="line">      </span><br><span class="line"><span class="comment"># 旧版</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">mandatory:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p>代码中开启:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitTemplate.setMandatory(<span class="literal">true</span>);</span><br></pre></td></tr></table></figure>



<p>上面“实战”概述</p>
<p>在仅开启了生产者确认机制的情况下，交换机接收到消息后，会直接给消息生产者发送确认消息，如果发现该消息不可路由，那么消息会被直接丢弃，此时生产者是不知道消息被丢弃这个事件的。</p>
<p>“回退消息”解决方案</p>
<p>那么如何让无法被路由的消息帮我想办法处理一下？最起码通知我一声，我好自己处理啊。通过设置 mandatory 参数可以在当消息传递过程中不可达目的地时将消息返回给生产者。</p>
<h5 id="实战-1"><a href="#实战-1" class="headerlink" title="实战"></a>实战</h5><p>修改配置文件</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">    <span class="attr">rabbitmq:</span></span><br><span class="line">        <span class="attr">host:</span> <span class="number">192.168</span><span class="number">.91</span><span class="number">.200</span></span><br><span class="line">        <span class="attr">port:</span> <span class="number">5672</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="number">123</span></span><br><span class="line">        <span class="attr">publisher-confirm-type:</span> <span class="string">correlated</span></span><br><span class="line">        <span class="attr">publisher-returns:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">template:</span></span><br><span class="line">            <span class="attr">mandatory:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">8888</span></span><br></pre></td></tr></table></figure>

<p><strong>修改回调接口</strong></p>
<p>实现 <code>RabbitTemplate.ReturnsCallback</code> 接口，并实现方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 回调接口</span></span><br><span class="line"><span class="comment"> */</span> </span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyCallBack</span> <span class="keyword">implements</span> <span class="title class_">RabbitTemplate</span>.ConfirmCallback,RabbitTemplate.ReturnsCallback &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//注入</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">//注入</span></span><br><span class="line">        rabbitTemplate.setConfirmCallback(<span class="built_in">this</span>);</span><br><span class="line">        rabbitTemplate.setReturnsCallback(<span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 交换机不管是否收到消息的一个回调方法</span></span><br><span class="line"><span class="comment">     * 1. 发消息 交换机接收到了 回调</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> correlationData  保存回调信息的Id及相关信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ack              交换机收到消息 为true</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cause            未收到消息的原因</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">confirm</span><span class="params">(CorrelationData correlationData, <span class="type">boolean</span> ack,String cause)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> correlationData!=<span class="literal">null</span>?correlationData.getId():<span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span>(ack)&#123;</span><br><span class="line">            log.info(<span class="string">&quot;交换机已经收到了ID为:&#123;&#125;的消息&quot;</span>,id);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;交换机还未收到ID为:&#123;&#125;的消息，由于原因:&#123;&#125;&quot;</span>,id,cause);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//可以在当消息传递过程中不可达目的地时将消息返回给生产者</span></span><br><span class="line">    <span class="comment">//只有不可达目的地的时候 才进行回退</span></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当消息无法路由的时候的回调方法</span></span><br><span class="line"><span class="comment">     *  message      消息</span></span><br><span class="line"><span class="comment">     *  replyCode    编码</span></span><br><span class="line"><span class="comment">     *  replyText    退回原因</span></span><br><span class="line"><span class="comment">     *  exchange     从哪个交换机退回</span></span><br><span class="line"><span class="comment">     *  routingKey   通过哪个路由 key 退回</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">returnedMessage</span><span class="params">(ReturnedMessage returned)</span> &#123;</span><br><span class="line">        log.error(<span class="string">&quot;消息&#123;&#125;,被交换机&#123;&#125;退回，退回原因:&#123;&#125;,路由key:&#123;&#125;&quot;</span>,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">String</span>(returned.getMessage().getBody()),returned.getExchange(),</span><br><span class="line">                returned.getReplyText(),returned.getRoutingKey());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>测试</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">打开浏览器访问地址：http:<span class="comment">//localhost:8888/confirm/sendMessage/大家好1 </span></span><br></pre></td></tr></table></figure>

<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230511232548563.png"></p>
<h4 id="备份交换机"><a href="#备份交换机" class="headerlink" title="备份交换机"></a>备份交换机</h4><h5 id="介绍-3"><a href="#介绍-3" class="headerlink" title="介绍"></a>介绍</h5><p>有了 mandatory  参数和回退消息，我们获得了对无法投递消息的感知能力，有机会在生产者的消息无法被投递时发现并处理。但有时候，我们并不知道该如何处理这些无法路由的消息，最多打个日志，然后触发报警，再来手动处理。而通过日志来处理这些无法路由的消息是很不优雅的做法，特别是当生产者所在的服务有多台机器的时候，手动复制日志会更加麻烦而且容易出错。而且设置 mandatory 参数会增加生产者的复杂性，需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息，又不想增加生产者的复杂性，该怎么做呢？</p>
<p>前面在设置死信队列的文章中，我们提到，可以为队列设置死信交换机来存储那些处理失败的消息，可是这些不可路由消息根本没有机会进入到队列，因此无法使用死信队列来保存消息。 在 RabbitMQ 中，有一种备份交换机的机制存在，可以很好的应对这个问题。</p>
<p>什么是备份交换机呢？备份交换机可以理解为 RabbitMQ  中交换机的“备胎”，当我们为某一个交换机声明一个对应的备份交换机时，就是为它创建一个备胎，当交换机接收到一条不可路由消息时，将会把这条消息转发到备份交换机中，由备份交换机来进行转发和处理，通常备份交换机的类型为 Fanout ，这样就能把所有消息都投递到与其绑定的队列中，然后我们在备份交换机下绑定一个队列，这样所有那些原交换机无法被路由的消息，就会都进 入这个队列了。当然，我们还可以建立一个报警队列，用独立的消费者来进行监测和报警。</p>
<h5 id="实战-2"><a href="#实战-2" class="headerlink" title="实战"></a>实战</h5><p>需要一个备份交换机 <code>backup.exchange</code>，类型为 <code>fanout</code>，该交换机发送消息到队列 <code>backup.queue</code> 和 <code>warning.queue</code></p>
<p>代码结构图:</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230512100041867.png"></p>
<p><strong>配置类</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> frx</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2022/7/26  19:05</span></span><br><span class="line"><span class="comment"> * desc：配置类，发布确认(高级)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfirmConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换机</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;confirm_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;confirm_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//routingKey</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONFIRM_ROUTING_KEY</span> <span class="operator">=</span> <span class="string">&quot;key1&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//关于备份的</span></span><br><span class="line">    <span class="comment">//交换机</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">BACKUP_EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;backup_exchange&quot;</span>;</span><br><span class="line">    <span class="comment">//队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">BACKUP_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;backup_queue&quot;</span>;</span><br><span class="line">    <span class="comment">//报警队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">WARNING_QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;warning_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明交换机,设置该交换机的备份交换机</span></span><br><span class="line">    <span class="meta">@Bean(&quot;confirmExchange&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">confirmExchange</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME)</span><br><span class="line">                .durable(<span class="literal">true</span>).withArgument(<span class="string">&quot;alternate-exchange&quot;</span>,BACKUP_EXCHANGE_NAME).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明队列</span></span><br><span class="line">    <span class="meta">@Bean(&quot;confirmQueue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">confirmQueue</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绑定</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">queueBindingExchange</span><span class="params">(<span class="meta">@Qualifier(&quot;confirmQueue&quot;)</span> Queue confirmQueue,</span></span><br><span class="line"><span class="params">                                        <span class="meta">@Qualifier(&quot;confirmExchange&quot;)</span> DirectExchange confirmExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_ROUTING_KEY);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//备份交换机的创建</span></span><br><span class="line">    <span class="meta">@Bean(&quot;backupExchange&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> FanoutExchange <span class="title function_">backupExchange</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FanoutExchange</span>(BACKUP_EXCHANGE_NAME);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明备份队列</span></span><br><span class="line">    <span class="meta">@Bean(&quot;backupQueue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">backupQueue</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(BACKUP_QUEUE_NAME).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明报警队列</span></span><br><span class="line">    <span class="meta">@Bean(&quot;warningQueue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">warningQueue</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(WARNING_QUEUE_NAME).build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绑定 备份队列绑定备份交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">backupQueueBindingBackupExchange</span><span class="params">(<span class="meta">@Qualifier(&quot;backupQueue&quot;)</span> Queue backupQueue,</span></span><br><span class="line"><span class="params">                                        <span class="meta">@Qualifier(&quot;backupExchange&quot;)</span> FanoutExchange backupExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(backupQueue).to(backupExchange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//绑定 报警队列绑定备份交换机</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">warningQueueBindingBackupExchange</span><span class="params">(<span class="meta">@Qualifier(&quot;warningQueue&quot;)</span> Queue warningQueue,</span></span><br><span class="line"><span class="params">                                                    <span class="meta">@Qualifier(&quot;backupExchange&quot;)</span> FanoutExchange backupExchange)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(warningQueue).to(backupExchange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>报警消费者</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 报警消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WarningConsumer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//接收报警信息</span></span><br><span class="line">    <span class="meta">@RabbitListener(queues = ConfirmConfig.WARNING_QUEUE_NAME)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receiveWarningMsg</span><span class="params">(Message message)</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">        log.error(<span class="string">&quot;报警发现不可路由消息:&#123;&#125;&quot;</span>,msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于之前写过 <code>confirm.exchange</code> 交换机，当更改配置了，需要删掉，不然会报错</p>
<p><img src="D:\imags\typora_imags\image-20230512104416205.png" alt="image-20230512104416205"></p>
<p>Mandatory 参数与备份交换机可以一起使用的时候，如果两者同时开启，消息究竟何去何从？谁优先级高，经过上面结果显示答案是<strong>备份交换机优先级高</strong>。</p>
<h3 id="RabbitMQ-其他知识点"><a href="#RabbitMQ-其他知识点" class="headerlink" title="RabbitMQ 其他知识点"></a>RabbitMQ 其他知识点</h3><h4 id="幂等性"><a href="#幂等性" class="headerlink" title="幂等性"></a>幂等性</h4><h5 id="概念-2"><a href="#概念-2" class="headerlink" title="概念"></a>概念</h5><p>用户对于同一操作发起的一次请求或者多次请求的结果是一致的，不会因为多次点击而产生了副作用。举个最简单的例子，那就是支付，用户购买商品后支付，支付扣款成功，但是返回结果的时候网络异常，此时钱已经扣了，用户再次点击按钮，此时会进行第二次扣款，返回结果成功，用户查询余额发现多扣钱了，流水记录也变成了两条。在以前的单应用系统中，我们只需要把数据操作放入事务中即可，发生错误立即回滚，但是再响应客户端的时候也有可能出现网络中断或者异常等等。</p>
<p>可以理解为验证码，只能输入一次，再次重新输入会刷新验证码，原来的验证码失效。</p>
<h5 id="消息重复消费"><a href="#消息重复消费" class="headerlink" title="消息重复消费"></a>消息重复消费</h5><p>消费者在消费 MQ 中的消息时，MQ 已把消息发送给消费者，消费者在给 MQ 返回 ack 时网络中断， 故 MQ  未收到确认信息，该条消息会重新发给其他的消费者，或者在网络重连后再次发送给该消费者，但实际上该消费者已成功消费了该条消息，造成消费者消费了重复的消息。</p>
<h5 id="解决思路"><a href="#解决思路" class="headerlink" title="解决思路"></a>解决思路</h5><p>MQ 消费者的幂等性的解决一般使用<strong>全局 ID <strong>或者写个唯一标识比如时间戳 或者 UUID 或者订单消费者消费 MQ 中的消息也可利用 MQ 的该 id 来判断，或者可按自己的规则生成一个全局唯一 id，</strong>每次消费消息时用该 id 先判断该消息是否已消费过</strong></p>
<h5 id="消费端的幂等性保障"><a href="#消费端的幂等性保障" class="headerlink" title="消费端的幂等性保障"></a>消费端的幂等性保障</h5><p>在海量订单生成的业务高峰期，生产端有可能就会重复发生了消息，这时候消费端就要实现幂等性，这就意味着我们的消息永远不会被消费多次，即使我们收到了一样的消息。</p>
<p>业界主流的幂等性有两种操作：</p>
<ul>
<li>唯一 ID+ 指纹码机制,利用数据库主键去重</li>
</ul>
<p>指纹码：我们的一些规则或者时间戳加别的服务给到的唯一信息码,它并不一定是我们系统生成的，基本都是由我们的业务规则拼接而来，但是一定要保证唯一性，然后就利用查询语句进行判断这个 id  是否存在数据库中，优势就是实现简单就一个拼接，然后查询判断是否重复；劣势就是在高并发时，如果是单个数据库就会有写入性能瓶颈当然也可以采用分库分表提升性能，但也不是我们最推荐的方式。</p>
<ul>
<li>Redis 的原子性</li>
</ul>
<p>利用 redis 执行 setnx 命令，天然具有幂等性。从而实现不重复消费</p>
<h4 id="优先级队列"><a href="#优先级队列" class="headerlink" title="优先级队列"></a>优先级队列</h4><h5 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h5><p>在我们系统中有一个订单催付的场景，我们的客户在天猫下的订单，淘宝会及时将订单推送给我们，如果在用户设定的时间内未付款那么就会给用户推送一条短信提醒，很简单的一个功能对吧。</p>
<p>但是，tmall  商家对我们来说，肯定是要分大客户和小客户的对吧，比如像苹果，小米这样大商家一年起码能给我们创造很大的利润，所以理应当然，他们的订单必须得到优先处理，而曾经我们的后端系统是使用 redis 来存放的定时轮询，大家都知道 redis 只能用 List  做一个简简单单的消息队列，并不能实现一个优先级的场景，所以订单量大了后采用 RabbitMQ  进行改造和优化，如果发现是大客户的订单给一个相对比较高的优先级， 否则就是默认优先级。</p>
<h5 id="添加方法"><a href="#添加方法" class="headerlink" title="添加方法"></a>添加方法</h5><p><strong>Web页面添加</strong></p>
<ol>
<li>进入 Web 页面，点击 Queue 菜单，然后点击 <code>Add a new queue</code></li>
<li>点击下方的 <code>Maximum priority</code></li>
<li>执行第二步，则会自动在 <code>Argument</code> 生成 <code>x-max-priority</code> 字符串</li>
<li>点击 <code>Add queue</code> 即可添加优先级队列成功</li>
</ol>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230512112423411.png"></p>
<p><strong>声明队列时添加优先级</strong></p>
<p>设置队列的最大优先级 最大可以设置到 255 官网推荐 1-10 如果设置太高比较吃内存和 CPU</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Object&gt; params = <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line"><span class="comment">// 优先级为 10</span></span><br><span class="line">params.put(<span class="string">&quot;x-max-priority&quot;</span>, <span class="number">10</span>);</span><br><span class="line">channel.queueDeclare(<span class="string">&quot;hello&quot;</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, params);</span><br></pre></td></tr></table></figure>

<p>注意</p>
<blockquote>
<p>注意事项</p>
<p>队列实现优先级需要做的事情有如下：队列需要设置为优先级队列，消息需要设置消息的优先级，消费者需要等待消息已经发送到队列中才去消费，因为这样才有机会对消息进行排序</p>
</blockquote>
<h5 id="实战-3"><a href="#实战-3" class="headerlink" title="实战"></a>实战</h5><p>生产者发送十个消息，如果消息为 <code>info5</code>，则优先级是最高的，当消费者从队列获取消息的时候，优先获取 <code>info5</code> 消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 优先级 生产者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PriorityProducer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;priority_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//给消息赋予一个priority属性</span></span><br><span class="line">        AMQP.<span class="type">BasicProperties</span> <span class="variable">properties</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">AMQP</span>.BasicProperties().builder().priority(<span class="number">1</span>).priority(<span class="number">10</span>).build();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; <span class="number">11</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;info&quot;</span>+i;</span><br><span class="line">            <span class="keyword">if</span>(i==<span class="number">5</span>)&#123;</span><br><span class="line">                channel.basicPublish(<span class="string">&quot;&quot;</span>,QUEUE_NAME,properties,message.getBytes());</span><br><span class="line">            &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                channel.basicPublish(<span class="string">&quot;&quot;</span>,QUEUE_NAME,<span class="literal">null</span>,message.getBytes());</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;消息发送完成：&quot;</span>+message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 优先级 消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PriorityConsumer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;priority_queue&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> RabbitMQUtils.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置队列的最大优先级 最大可以设置到255 官网推荐1-10 如果设置太高比较吃内存和CPU</span></span><br><span class="line">        Map&lt;String, Object&gt; params = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        params.put(<span class="string">&quot;x-max-priority&quot;</span>,<span class="number">10</span>);</span><br><span class="line">        channel.queueDeclare(QUEUE_NAME,<span class="literal">true</span>,<span class="literal">false</span>,<span class="literal">false</span>,params);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//推送消息如何进行消费的接口回调</span></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, delivery) -&gt;&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(delivery.getBody());</span><br><span class="line">            System.out.println(<span class="string">&quot;消费的消息: &quot;</span>+message);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//取消消费的一个回调接口 如在消费的时候队列被删除掉了</span></span><br><span class="line">        <span class="type">CancelCallback</span> <span class="variable">cancelCallback</span> <span class="operator">=</span> (consumerTag) -&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消息消费被中断&quot;</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        channel.basicConsume(QUEUE_NAME,<span class="literal">true</span>,deliverCallback,cancelCallback);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>效果</strong></p>
<p>nfo 5 的优先级为 10，优先级最高。消费者消费信息效果如图：</p>
<p><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/image-20230513101236462.png"></p>
<h4 id="惰性队列"><a href="#惰性队列" class="headerlink" title="惰性队列"></a>惰性队列</h4><h5 id="使用场景-1"><a href="#使用场景-1" class="headerlink" title="使用场景"></a>使用场景</h5><p>​        RabbitMQ 从 3.6.0  版本开始引入了惰性队列的概念。惰性队列会尽可能的将消息存入磁盘中，而在消费者消费到相应的消息时才会被加载到内存中，它的一个重要的设计目标是能够<strong>支持更长的队列，即支持更多的消息存储</strong>。当<strong>消费者由于各种各样的原因(比如消费者下线、宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成堆积时，惰性队列就很有必要了</strong>。</p>
<p>​        默认情况下，当生产者将消息发送到 RabbitMQ  的时候，队列中的消息会尽可能的存储在内存之中，这样可以更加快速的将消息发送给消费者。即使是持久化的消息，在被写入磁盘的同时也会在内存中驻留一份备份。当 RabbitMQ 需要释放内存的时候，会将内存中的消息换页至磁盘中，这个操作会<strong>耗费较长的时间，也会阻塞队列的操作</strong>，进而无法接收新的消息。虽然  RabbitMQ 的开发者们一直在升级相关的算法， 但是效果始终不太理想，尤其是在消息量特别大的时候。</p>
<h5 id="两种模式"><a href="#两种模式" class="headerlink" title="两种模式"></a>两种模式</h5><p>队列具备两种模式：default 和 lazy。默认的为 default 模式，在 3.6.0 之前的版本无需做任何变更。lazy 模式即为惰性队列的模式，可以通过调用 <code>channel.queueDeclare</code> 方法的时候在参数中设置，也可以通过 Policy 的方式设置，如果一个队列同时使用这两种方式设置的话，那么 Policy 的方式具备更高的优先级。如果要通过声明的方式改变已有队列的模式的话，那么只能先删除队列，然后再重新声明一个新的。</p>
<p>在队列声明的时候可以通过 <code>x-queue-mode</code> 参数来设置队列的模式，取值为 default 和 lazy。下面示例中演示了一个惰性队列的声明细节：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Object&gt; args = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, Object&gt;();</span><br><span class="line">args.put(<span class="string">&quot;x-queue-mode&quot;</span>, <span class="string">&quot;lazy&quot;</span>);</span><br><span class="line">channel.queueDeclare(<span class="string">&quot;myqueue&quot;</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, args);</span><br></pre></td></tr></table></figure>



<p>也可以在 Web 页面添加队列时，选择 <code>Lazy mode</code></p>
<h5 id="内存开销对比"><a href="#内存开销对比" class="headerlink" title="内存开销对比"></a>内存开销对比</h5><p>在发送 1 百万条消息，每条消息大概占 1KB 的情况下，普通队列占用内存是 1.2GB，而惰性队列仅仅占用 1.5MB</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">Author: </span><span class="post-copyright-info"><a href="https://gitee.com/CWH6">cwh</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">Link: </span><span class="post-copyright-info"><a href="https://gitee.com/CWH6/2023/05/05/%E3%80%90RabbitMQ%E3%80%91%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/">https://gitee.com/CWH6/2023/05/05/%E3%80%90RabbitMQ%E3%80%91%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">Copyright Notice: </span><span class="post-copyright-info">All articles in this blog are licensed under <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> unless stating additionally.</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/bk/tags/MQ/">MQ</a></div><div class="post_share"><div class="social-share" data-image="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/xhs.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/bk/2023/05/07/%E3%80%90MQ%E3%80%91MQ%E6%A6%82%E8%BF%B0/" title="【MQ】MQ概念与选用"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">Previous Post</div><div class="prev_info">【MQ】MQ概念与选用</div></div></a></div><div class="next-post pull-right"><a href="/bk/2023/05/04/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" title="【设计模式】23 种设计模式"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">Next Post</div><div class="next_info">【设计模式】23 种设计模式</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>Related Articles</span></div><div class="relatedPosts-list"><div><a href="/bk/2023/05/07/%E3%80%90MQ%E3%80%91MQ%E6%A6%82%E8%BF%B0/" title="【MQ】MQ概念与选用"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-05-07</div><div class="title">【MQ】MQ概念与选用</div></div></a></div><div><a href="/bk/2023/05/14/%E3%80%90%E5%AE%9E%E6%88%98%E3%80%91RabbitMQ%E8%AE%A2%E5%8D%95%E6%9C%AA%E6%94%AF%E4%BB%98x%E5%88%86%E9%92%9F%E8%87%AA%E5%8A%A8%E5%8F%96%E6%B6%88/" title="【实战】RabbitMQ订单未支付x分钟自动取消"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-05-14</div><div class="title">【实战】RabbitMQ订单未支付x分钟自动取消</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/xhs.png" onerror="this.onerror=null;this.src='/bk/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">cwh</div><div class="author-info__description">新博客地址：http://cwh6.gitee.io/vuepressv2/</div></div><div class="card-info-data site-data is-center"><a href="/bk/archives/"><div class="headline">Articles</div><div class="length-num">66</div></a><a href="/bk/tags/"><div class="headline">Tags</div><div class="length-num">33</div></a><a href="/bk/categories/"><div class="headline">Categories</div><div class="length-num">8</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="mailto:2747758546@qq.com" target="_blank" title="Email"><i class="fas fa-envelope" style="color: #4a7dbe;"></i></a><a class="social-icon" href="https://cwh6-bucket.oss-cn-shanghai.aliyuncs.com/bk/wx_self.jpg" target="_blank" title="wx"><i class="fas fa-qq" style="color: #4a7dbe;"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>Announcement</span></div><div class="announcement_content">ありがとう、私の暗い世界の小さな太陽、ありがとう、ずっと温めてくれた</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>Catalog</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E6%A6%82%E5%BF%B5"><span class="toc-number">1.</span> <span class="toc-text">RabbitMQ概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9B%E5%A4%A7%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5"><span class="toc-number">2.</span> <span class="toc-text">四大核心概念</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%9F%E4%BA%A7%E8%80%85"><span class="toc-number">2.1.</span> <span class="toc-text">生产者</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">2.2.</span> <span class="toc-text">交换机</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%98%9F%E5%88%97"><span class="toc-number">2.3.</span> <span class="toc-text">队列</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B6%88%E8%B4%B9%E8%80%85"><span class="toc-number">2.4.</span> <span class="toc-text">消费者</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%8E%9F%E7%90%86"><span class="toc-number">3.</span> <span class="toc-text">RabbitMQ模式与原理</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A8%A1%E5%BC%8F"><span class="toc-number">3.1.</span> <span class="toc-text">模式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%90%86"><span class="toc-number">3.2.</span> <span class="toc-text">原理</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85"><span class="toc-number">4.</span> <span class="toc-text">安装</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Linux%E4%B8%8B%E5%AE%89%E8%A3%85MQ"><span class="toc-number">4.1.</span> <span class="toc-text">Linux下安装MQ</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Docker%E5%AE%89%E8%A3%85MQ"><span class="toc-number">4.2.</span> <span class="toc-text">Docker安装MQ</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HelloWrold"><span class="toc-number">5.</span> <span class="toc-text">HelloWrold</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%9F%E4%BA%A7%E8%80%85%E4%BB%A3%E7%A0%81"><span class="toc-number">5.1.</span> <span class="toc-text">生产者代码</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B6%88%E6%81%AF%E6%B6%88%E8%B4%B9%E8%80%85"><span class="toc-number">5.2.</span> <span class="toc-text">消息消费者</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E5%B7%A5%E5%85%B7%E7%B1%BB"><span class="toc-number">6.</span> <span class="toc-text">RabbitMQ工具类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E9%98%9F%E5%88%97%E4%B8%8E%E8%BD%AE%E8%AF%A2"><span class="toc-number">7.</span> <span class="toc-text">工作队列与轮询</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B7%A5%E4%BD%9C%E9%98%9F%E5%88%97-Work-Queues"><span class="toc-number">7.1.</span> <span class="toc-text">工作队列 Work Queues</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BD%AE%E8%AF%A2%E6%B6%88%E8%B4%B9"><span class="toc-number">7.2.</span> <span class="toc-text">轮询消费</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B6%88%E8%B4%B9%E8%80%85%E8%BD%AE%E8%AE%AD%E6%B6%88%E8%B4%B9%E6%A1%88%E4%BE%8B"><span class="toc-number">7.3.</span> <span class="toc-text">消费者轮训消费案例</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E5%BA%94%E7%AD%94%E4%B8%8E%E5%8F%91%E5%B8%83"><span class="toc-number">8.</span> <span class="toc-text">RabbitMQ应答与发布</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%8A%A8%E5%BA%94%E7%AD%94"><span class="toc-number">8.1.</span> <span class="toc-text">自动应答</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%8B%E5%8A%A8%E5%BA%94%E7%AD%94"><span class="toc-number">8.2.</span> <span class="toc-text">手动应答</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%89%8B%E5%8A%A8%E5%BA%94%E7%AD%94%E6%96%B9%E6%B3%95"><span class="toc-number">8.2.1.</span> <span class="toc-text">手动应答方法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%89%8B%E5%8A%A8%E5%BA%94%E7%AD%94%E7%9A%84%E6%89%B9%E5%A4%84%E7%90%86"><span class="toc-number">8.2.2.</span> <span class="toc-text">手动应答的批处理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B6%88%E6%81%AF%E8%87%AA%E5%8A%A8%E4%BB%8E%E6%96%B0%E5%85%A5%E9%98%9F"><span class="toc-number">8.2.3.</span> <span class="toc-text">消息自动从新入队</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%8B%E5%8A%A8%E5%BA%94%E7%AD%94%E6%A1%88%E4%BE%8B"><span class="toc-number">8.3.</span> <span class="toc-text">手动应答案例</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E7%A1%AE%E8%AE%A4"><span class="toc-number">8.4.</span> <span class="toc-text">发布确认</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E7%A1%AE%E8%AE%A4%E7%9A%84%E7%AD%96%E7%95%A5"><span class="toc-number">8.4.1.</span> <span class="toc-text">发布确认的策略</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8D%95%E4%B8%AA%E7%A1%AE%E8%AE%A4%E5%8F%91%E5%B8%83"><span class="toc-number">8.4.2.</span> <span class="toc-text">单个确认发布</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%89%B9%E9%87%8F%E7%A1%AE%E8%AE%A4%E5%8F%91%E5%B8%83"><span class="toc-number">8.4.3.</span> <span class="toc-text">批量确认发布</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E7%A1%AE%E8%AE%A4%E5%8F%91%E5%B8%83"><span class="toc-number">8.4.4.</span> <span class="toc-text">异步确认发布</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%A4%84%E7%90%86%E5%BC%82%E6%AD%A5%E6%9C%AA%E7%A1%AE%E8%AE%A4%E6%B6%88%E6%81%AF"><span class="toc-number">8.4.5.</span> <span class="toc-text">处理异步未确认消息</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%89%E7%A7%8D%E7%A1%AE%E8%AE%A4%E5%8F%91%E5%B8%83%E5%AF%B9%E6%AF%94"><span class="toc-number">8.4.6.</span> <span class="toc-text">三种确认发布对比</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BA%94%E7%AD%94%E5%92%8C%E5%8F%91%E5%B8%83%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">8.5.</span> <span class="toc-text">应答和发布的区别</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RbbitMQ-%E6%8C%81%E4%B9%85%E5%8C%96"><span class="toc-number">9.</span> <span class="toc-text">RbbitMQ 持久化</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%8C%81%E4%B9%85%E5%8C%96"><span class="toc-number">9.1.</span> <span class="toc-text">队列实现持久化</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B6%88%E6%81%AF%E5%AE%9E%E7%8E%B0%E6%8C%81%E4%B9%85%E5%8C%96"><span class="toc-number">9.2.</span> <span class="toc-text">消息实现持久化</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E4%B8%8D%E5%85%AC%E5%88%86%E5%8F%91%E4%B8%8E%E9%A2%84%E5%8F%96%E5%80%BC"><span class="toc-number">10.</span> <span class="toc-text">RabbitMQ不公分发与预取值</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8D%E5%85%AC%E5%B9%B3%E5%88%86%E5%8F%91"><span class="toc-number">10.1.</span> <span class="toc-text">不公平分发</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A2%84%E5%8F%96%E5%80%BC"><span class="toc-number">10.2.</span> <span class="toc-text">预取值</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">11.</span> <span class="toc-text">RabbitMQ交换机</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5"><span class="toc-number">11.1.</span> <span class="toc-text">概念</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%97%A0%E5%90%8Dexchange"><span class="toc-number">11.2.</span> <span class="toc-text">无名exchange</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%B4%E6%97%B6%E9%98%9F%E5%88%97"><span class="toc-number">11.3.</span> <span class="toc-text">临时队列</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%8E%E5%8F%B0%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%BB%91%E5%AE%9A%E9%98%9F%E5%88%97"><span class="toc-number">11.4.</span> <span class="toc-text">后台交换机绑定队列</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84%E7%B1%BB%E5%9E%8B"><span class="toc-number">11.5.</span> <span class="toc-text">交换机的类型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Fanout-%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">11.6.</span> <span class="toc-text">Fanout 交换机</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#direct-%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">11.7.</span> <span class="toc-text">direct 交换机</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D"><span class="toc-number">11.7.1.</span> <span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%A4%9A%E9%87%8D%E7%BB%91%E5%AE%9A"><span class="toc-number">11.7.2.</span> <span class="toc-text">多重绑定</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Direct%E5%AE%9E%E6%88%98"><span class="toc-number">11.7.3.</span> <span class="toc-text">Direct实战</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Topics-%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">11.8.</span> <span class="toc-text">Topics 交换机</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D-1"><span class="toc-number">11.8.1.</span> <span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Topic-%E7%9A%84%E8%A6%81%E6%B1%82"><span class="toc-number">11.8.2.</span> <span class="toc-text">Topic 的要求</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Topic%E5%8C%B9%E9%85%8D%E6%A1%88%E4%BE%8B"><span class="toc-number">11.8.3.</span> <span class="toc-text">Topic匹配案例</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97"><span class="toc-number">12.</span> <span class="toc-text">死信队列</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5-1"><span class="toc-number">12.1.</span> <span class="toc-text">概念</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-number">12.2.</span> <span class="toc-text">应用场景</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AD%BB%E4%BF%A1%E7%9A%84%E6%9D%A5%E6%BA%90"><span class="toc-number">12.3.</span> <span class="toc-text">死信的来源</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AD%BB%E4%BF%A1%E5%AE%9E%E6%88%98"><span class="toc-number">12.4.</span> <span class="toc-text">死信实战</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B6%88%E6%81%AFTTL%E8%BF%87%E6%9C%9F"><span class="toc-number">12.4.1.</span> <span class="toc-text">消息TTL过期</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%AD%BB%E4%BF%A1%E6%9C%80%E5%A4%A7%E9%95%BF%E5%BA%A6"><span class="toc-number">12.4.2.</span> <span class="toc-text">死信最大长度</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%A7%81%E4%BF%A1%E6%B6%88%E6%81%AF%E8%A2%AB%E6%8B%92"><span class="toc-number">12.4.3.</span> <span class="toc-text">私信消息被拒</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%AD%BB%E4%BF%A1%E6%B6%88%E6%81%AF%E8%A2%AB%E6%8B%92"><span class="toc-number">12.4.4.</span> <span class="toc-text">死信消息被拒</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97"><span class="toc-number">13.</span> <span class="toc-text">延迟队列</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97%E6%A6%82%E5%BF%B5%EF%BC%9A"><span class="toc-number">13.1.</span> <span class="toc-text">延迟队列概念：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9A"><span class="toc-number">13.2.</span> <span class="toc-text">延迟队列使用场景：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#TTL%E7%9A%84%E4%B8%A4%E7%A7%8D%E8%AE%BE%E7%BD%AE"><span class="toc-number">13.3.</span> <span class="toc-text">TTL的两种设置</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B4%E5%90%88SpringBoot"><span class="toc-number">13.4.</span> <span class="toc-text">整合SpringBoot</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%98%9F%E5%88%97TTL"><span class="toc-number">13.5.</span> <span class="toc-text">队列TTL</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81%E6%9E%B6%E6%9E%84%E5%9B%BE"><span class="toc-number">13.5.1.</span> <span class="toc-text">代码架构图</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BB%B6%E6%97%B6%E9%98%9F%E5%88%97TTL%E4%BC%98%E5%8C%96"><span class="toc-number">13.6.</span> <span class="toc-text">延时队列TTL优化</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#RabbitMQ%E6%8F%92%E4%BB%B6%E5%AE%9E%E7%8E%B0%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97"><span class="toc-number">13.7.</span> <span class="toc-text">RabbitMQ插件实现延迟队列</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8F%92%E4%BB%B6%E5%AE%9E%E6%88%98"><span class="toc-number">13.8.</span> <span class="toc-text">插件实战</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E7%A1%AE%E8%AE%A4%E9%AB%98%E7%BA%A7"><span class="toc-number">14.</span> <span class="toc-text">发布确认高级</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E7%A1%AE%E8%AE%A4SpringBoot%E7%89%88%E6%9C%AC"><span class="toc-number">14.1.</span> <span class="toc-text">发布确认SpringBoot版本</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D-2"><span class="toc-number">14.2.</span> <span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9E%E6%88%98"><span class="toc-number">14.3.</span> <span class="toc-text">实战</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9B%9E%E9%80%80%E6%B6%88%E6%81%AF"><span class="toc-number">14.4.</span> <span class="toc-text">回退消息</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%AE%9E%E6%88%98-1"><span class="toc-number">14.4.1.</span> <span class="toc-text">实战</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A4%87%E4%BB%BD%E4%BA%A4%E6%8D%A2%E6%9C%BA"><span class="toc-number">14.5.</span> <span class="toc-text">备份交换机</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D-3"><span class="toc-number">14.5.1.</span> <span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%AE%9E%E6%88%98-2"><span class="toc-number">14.5.2.</span> <span class="toc-text">实战</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RabbitMQ-%E5%85%B6%E4%BB%96%E7%9F%A5%E8%AF%86%E7%82%B9"><span class="toc-number">15.</span> <span class="toc-text">RabbitMQ 其他知识点</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B9%82%E7%AD%89%E6%80%A7"><span class="toc-number">15.1.</span> <span class="toc-text">幂等性</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5-2"><span class="toc-number">15.1.1.</span> <span class="toc-text">概念</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B6%88%E6%81%AF%E9%87%8D%E5%A4%8D%E6%B6%88%E8%B4%B9"><span class="toc-number">15.1.2.</span> <span class="toc-text">消息重复消费</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%80%9D%E8%B7%AF"><span class="toc-number">15.1.3.</span> <span class="toc-text">解决思路</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B6%88%E8%B4%B9%E7%AB%AF%E7%9A%84%E5%B9%82%E7%AD%89%E6%80%A7%E4%BF%9D%E9%9A%9C"><span class="toc-number">15.1.4.</span> <span class="toc-text">消费端的幂等性保障</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97"><span class="toc-number">15.2.</span> <span class="toc-text">优先级队列</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-number">15.2.1.</span> <span class="toc-text">使用场景</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B7%BB%E5%8A%A0%E6%96%B9%E6%B3%95"><span class="toc-number">15.2.2.</span> <span class="toc-text">添加方法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%AE%9E%E6%88%98-3"><span class="toc-number">15.2.3.</span> <span class="toc-text">实战</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%83%B0%E6%80%A7%E9%98%9F%E5%88%97"><span class="toc-number">15.3.</span> <span class="toc-text">惰性队列</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF-1"><span class="toc-number">15.3.1.</span> <span class="toc-text">使用场景</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%A4%E7%A7%8D%E6%A8%A1%E5%BC%8F"><span class="toc-number">15.3.2.</span> <span class="toc-text">两种模式</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E5%BC%80%E9%94%80%E5%AF%B9%E6%AF%94"><span class="toc-number">15.3.3.</span> <span class="toc-text">内存开销对比</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>Recent Post</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/bk/2024/02/05/SpringIOC%E7%9A%84%E7%90%86%E8%A7%A3/" title="SpringIOC的理解">SpringIOC的理解</a><time datetime="2024-02-05T14:26:55.000Z" title="Created 2024-02-05 22:26:55">2024-02-05</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/bk/2024/01/18/%E3%80%90ai%E3%80%91chatgpt%E7%BB%95%E5%BC%80%E6%A3%80%E6%9F%A5%E8%BF%87%E6%BB%A4/" title="【ai】chatgpt绕开检查过滤">【ai】chatgpt绕开检查过滤</a><time datetime="2024-01-18T15:55:53.000Z" title="Created 2024-01-18 23:55:53">2024-01-18</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/bk/2024/01/15/%E3%80%90ai%E6%A8%A1%E5%9E%8B%E3%80%91%E5%88%A4%E6%96%AD%E8%AF%AD%E5%8F%A5%E7%9A%84%E7%A7%AF%E6%9E%81%E6%80%A7%E4%B8%8E%E6%B6%88%E6%9E%81%E6%80%A7/" title="【ai模型】判断语句的积极性与消极性">【ai模型】判断语句的积极性与消极性</a><time datetime="2024-01-15T13:10:14.000Z" title="Created 2024-01-15 21:10:14">2024-01-15</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/bk/2023/12/21/%E3%80%90%E5%8C%BA%E5%9D%97%E9%93%BE%E3%80%91%E4%BB%A5%E5%A4%AA%E5%9D%8A%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6/" title="【区块链】以太坊智能合约">【区块链】以太坊智能合约</a><time datetime="2023-12-21T13:36:30.000Z" title="Created 2023-12-21 21:36:30">2023-12-21</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/bk/2023/12/21/%E3%80%90Activiti7%E3%80%91%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%BD%BF%E7%94%A8/" title="【Activiti7】工作流使用">【Activiti7】工作流使用</a><time datetime="2023-12-21T13:21:01.000Z" title="Created 2023-12-21 21:21:01">2023-12-21</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2024 By cwh</div><div class="framework-info"><span>Framework </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>Theme </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="Read Mode"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="Switch Between Light And Dark Mode"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="Toggle between single-column and double-column"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="Setting"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="Table Of Contents"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="Back To Top"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/bk/js/utils.js"></script><script src="/bk/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.umd.min.js"></script><div class="js-pjax"></div><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-show-text.min.js" data-mobile="false" data-text="offer" data-fontsize="15px" data-random="false" async="async"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>